/*!
 * \file beidou_b3i_telemetry_decoder_gs.cc
 * \brief Implementation of an adapter of a BEIDOU B31 DNAV data decoder block
 * \author Damian Miralles, 2019. dmiralles2009(at)gmail.com
 *
 * -----------------------------------------------------------------------------
 *
 * 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)
 * SPDX-License-Identifier: GPL-3.0-or-later
 *
 * -----------------------------------------------------------------------------
 */

#include "beidou_b3i_telemetry_decoder_gs.h"
#include "Beidou_B3I.h"
#include "Beidou_DNAV.h"
#include "beidou_dnav_almanac.h"
#include "beidou_dnav_ephemeris.h"
#include "beidou_dnav_iono.h"
#include "beidou_dnav_utc_model.h"
#include "display.h"
#include "gnss_sdr_make_unique.h"  // for std::make_unique in C++11
#include "gnss_synchro.h"
#include "tlm_crc_stats.h"
#include "tlm_utils.h"
#include "tow_to_trk.h"
#include <pmt/pmt.h>        // for make_any
#include <pmt/pmt_sugar.h>  // for mp
#include <cstddef>          // for size_t
#include <cstdlib>          // for abs
#include <exception>        // for exception
#include <iomanip>          // for setprecision
#include <iostream>         // for cout

#if USE_GLOG_AND_GFLAGS
#include <glog/logging.h>
#else
#include <absl/log/log.h>
#endif

#define CRC_ERROR_LIMIT 8

beidou_b3i_telemetry_decoder_gs_sptr
beidou_b3i_make_telemetry_decoder_gs(const Gnss_Satellite &satellite,
    const Tlm_Conf &conf)
{
    return beidou_b3i_telemetry_decoder_gs_sptr(new beidou_b3i_telemetry_decoder_gs(satellite, conf));
}


beidou_b3i_telemetry_decoder_gs::beidou_b3i_telemetry_decoder_gs(
    const Gnss_Satellite &satellite, const Tlm_Conf &conf)
    : telemetry_impl_interface("beidou_b3i_telemetry_decoder_gs",
          gr::io_signature::make(1, 1, sizeof(Gnss_Synchro)),
          gr::io_signature::make(1, 1, sizeof(Gnss_Synchro))),
      d_dump_filename(conf.dump_filename),
      d_sample_counter(0),
      d_preamble_index(0),
      d_required_symbols(BEIDOU_DNAV_SUBFRAME_SYMBOLS + BEIDOU_DNAV_PREAMBLE_LENGTH_SYMBOLS),
      d_stat(0),
      d_channel(0),
      d_CRC_error_counter(0),
      d_symbols_per_preamble(BEIDOU_DNAV_PREAMBLE_LENGTH_SYMBOLS),
      d_samples_per_preamble(BEIDOU_DNAV_PREAMBLE_LENGTH_SYMBOLS),
      d_preamble_period_samples(BEIDOU_DNAV_PREAMBLE_PERIOD_SYMBOLS),
      d_last_valid_preamble(0),
      d_symbol_duration_ms(BEIDOU_B3I_TELEMETRY_SYMBOLS_PER_BIT * BEIDOU_B3I_CODE_PERIOD_MS),
      d_TOW_at_Preamble_ms(0U),
      d_TOW_at_current_symbol_ms(0U),
      d_flag_SOW_set(false),
      d_flag_frame_sync(false),
      d_flag_preamble(false),
      d_flag_valid_word(false),
      d_sent_tlm_failed_msg(false),
      d_dump(conf.dump),
      d_dump_mat(conf.dump_mat),
      d_remove_dat(conf.remove_dat),
      d_enable_navdata_monitor(conf.enable_navdata_monitor),
      d_dump_crc_stats(conf.dump_crc_stats),
      d_tow_to_trk(conf.tow_to_trk)
{
    configure_basic_outputs();

    if (d_enable_navdata_monitor)
        {
            // register nav message monitor out
            this->message_port_register_out(pmt::mp("Nav_msg_from_TLM"));
            d_nav_msg_packet.system = std::string("C");
            d_nav_msg_packet.signal = std::string("B3");
        }

    d_satellite = Gnss_Satellite(satellite.get_system(), satellite.get_PRN());
    LOG(INFO) << "Initializing BeiDou B3I Telemetry Decoding for satellite " << this->d_satellite;

    // Setting samples of preamble code
    for (int32_t i = 0; i < d_symbols_per_preamble; i++)
        {
            if (BEIDOU_DNAV_PREAMBLE[i] == '1')
                {
                    d_preamble_samples[i] = 1;
                }
            else
                {
                    d_preamble_samples[i] = -1;
                }
        }

    d_symbol_history.set_capacity(d_required_symbols);

    if (d_dump_crc_stats)
        {
            // initialize the telemetry CRC statistics class
            d_Tlm_CRC_Stats = std::make_unique<Tlm_CRC_Stats>();
            d_Tlm_CRC_Stats->initialize(conf.dump_crc_stats_filename);
        }
    else
        {
            d_Tlm_CRC_Stats = nullptr;
        }
}


beidou_b3i_telemetry_decoder_gs::~beidou_b3i_telemetry_decoder_gs()
{
    DLOG(INFO) << "BeiDou B3I Telemetry decoder block (channel " << d_channel << ") destructor called.";
    size_t pos = 0;
    if (d_dump_file.is_open() == true)
        {
            pos = d_dump_file.tellp();
            try
                {
                    d_dump_file.close();
                }
            catch (const std::exception &ex)
                {
                    LOG(WARNING) << "Exception in destructor closing the dump file " << ex.what();
                }
            if (pos == 0)
                {
                    if (!tlm_remove_file(d_dump_filename))
                        {
                            LOG(WARNING) << "Error deleting temporary file";
                        }
                }
        }
    if (d_dump && (pos != 0) && d_dump_mat)
        {
            save_tlm_matfile(d_dump_filename);
            if (d_remove_dat)
                {
                    if (!tlm_remove_file(d_dump_filename))
                        {
                            LOG(WARNING) << "Error deleting temporary file";
                        }
                }
        }
}


void beidou_b3i_telemetry_decoder_gs::decode_bch15_11_01(const int32_t *bits,
    std::array<int32_t, 15> &decbits)
{
    int32_t bit;
    int32_t err;
    std::array<int32_t, 4> reg{-1, -1, -1, -1};
    const std::array<int32_t, 15> errind{14, 13, 10, 12, 6, 9, 4, 11, 0, 5, 7, 8, 1, 3, 2};

    for (uint32_t i = 0; i < 15; i++)
        {
            decbits[i] = bits[i];
        }

    for (uint32_t i = 0; i < 15; i++)
        {
            bit = reg[3];
            reg[3] = reg[2];
            reg[2] = reg[1];
            reg[1] = reg[0];
            reg[0] = bits[i] * bit;
            reg[1] *= bit;
        }

    for (uint32_t i = 0; i < 4; ++i)
        {
            reg[i] = (reg[i] + 1) / 2;
        }

    err = reg[0] + reg[1] * 2 + reg[2] * 4 + reg[3] * 8;

    if (err > 0 && err < 16)
        {
            decbits[errind[err - 1]] *= -1;
        }
}


void beidou_b3i_telemetry_decoder_gs::decode_word(
    int32_t word_counter,
    const float *enc_word_symbols,
    int32_t *dec_word_symbols)
{
    std::array<int32_t, 30> bitsbch{};
    std::array<int32_t, 15> first_branch{};
    std::array<int32_t, 15> second_branch{};

    if (word_counter == 1)
        {
            for (uint32_t j = 0; j < 30; j++)
                {
                    dec_word_symbols[j] = static_cast<int32_t>(enc_word_symbols[j] > 0) ? (1) : (-1);
                }
        }
    else
        {
            for (uint32_t r = 0; r < 2; r++)
                {
                    for (uint32_t c = 0; c < 15; c++)
                        {
                            bitsbch[r * 15 + c] = static_cast<int32_t>(enc_word_symbols[c * 2 + r] > 0) ? (1) : (-1);
                        }
                }

            decode_bch15_11_01(&bitsbch[0], first_branch);
            decode_bch15_11_01(&bitsbch[15], second_branch);

            for (uint32_t j = 0; j < 11; j++)
                {
                    dec_word_symbols[j] = first_branch[j];
                    dec_word_symbols[j + 11] = second_branch[j];
                }

            for (uint32_t j = 0; j < 4; j++)
                {
                    dec_word_symbols[j + 22] = first_branch[11 + j];
                    dec_word_symbols[j + 26] = second_branch[11 + j];
                }
        }
}


void beidou_b3i_telemetry_decoder_gs::decode_subframe(float *frame_symbols, double cn0)
{
    // 1. Transform from symbols to bits
    std::string data_bits;
    data_bits.reserve(BEIDOU_DNAV_WORDS_SUBFRAME * BEIDOU_DNAV_WORD_LENGTH_BITS);
    std::array<int32_t, 30> dec_word_bits{};

    // Decode each word in subframe
    for (uint32_t ii = 0; ii < BEIDOU_DNAV_WORDS_SUBFRAME; ii++)
        {
            // decode the word
            decode_word((ii + 1), &frame_symbols[ii * 30], dec_word_bits.data());

            // Save word to string format
            for (uint32_t jj = 0; jj < (BEIDOU_DNAV_WORD_LENGTH_BITS); jj++)
                {
                    data_bits.push_back((dec_word_bits[jj] > 0) ? ('1') : ('0'));
                }
        }

    if (d_enable_navdata_monitor)
        {
            d_nav_msg_packet.nav_message = data_bits;
        }

    if (d_satellite.get_PRN() > 0 && d_satellite.get_PRN() < 6)
        {
            d_nav.d2_subframe_decoder(data_bits);
        }
    else
        {
            d_nav.d1_subframe_decoder(data_bits);
        }

    // 3. Check operation executed correctly
    bool crc_ok = d_nav.get_flag_CRC_test();
    if (crc_ok)
        {
            DLOG(INFO) << "BeiDou DNAV CRC correct in channel " << d_channel
                       << " from satellite " << d_satellite << " with CN0=" << cn0 << " dB-Hz";
        }
    else
        {
            DLOG(INFO) << "BeiDou DNAV CRC error in channel " << d_channel
                       << " from satellite " << d_satellite << " with CN0=" << cn0 << " dB-Hz";
        }
    if (d_dump_crc_stats)
        {
            // update CRC statistics
            d_Tlm_CRC_Stats->update_CRC_stats(crc_ok);
        }
    // 4. Push the new navigation data to the queues
    if (d_nav.have_new_ephemeris() == true)
        {
            // get object for this SV (mandatory)
            const std::shared_ptr<Beidou_Dnav_Ephemeris> tmp_obj =
                std::make_shared<Beidou_Dnav_Ephemeris>(d_nav.get_ephemeris());
            this->message_port_pub(pmt::mp("telemetry"), pmt::make_any(tmp_obj));
            LOG(INFO) << "BEIDOU DNAV Ephemeris have been received in channel"
                      << d_channel << " from satellite " << d_satellite << " with CN0=" << cn0 << " dB-Hz";
#if __cplusplus == 201103L
            const int default_precision = std::cout.precision();
#else
            const auto default_precision{std::cout.precision()};
#endif
            std::cout << TEXT_YELLOW << "New BEIDOU B3I DNAV message received in channel " << d_channel
                      << ": ephemeris from satellite " << d_satellite
                      << " with CN0=" << std::setprecision(2) << cn0 << std::setprecision(default_precision)
                      << " dB-Hz" << TEXT_RESET << std::endl;
        }
    if (d_nav.have_new_utc_model() == true)
        {
            // get object for this SV (mandatory)
            const std::shared_ptr<Beidou_Dnav_Utc_Model> tmp_obj =
                std::make_shared<Beidou_Dnav_Utc_Model>(d_nav.get_utc_model());
            this->message_port_pub(pmt::mp("telemetry"), pmt::make_any(tmp_obj));
            LOG(INFO) << "BEIDOU DNAV UTC Model data have been received in channel"
                      << d_channel << " from satellite " << d_satellite;
#if __cplusplus == 201103L
            const int default_precision = std::cout.precision();
#else
            const auto default_precision{std::cout.precision()};
#endif
            std::cout << TEXT_YELLOW << "New BEIDOU B3I DNAV utc model message received in channel "
                      << d_channel << ": UTC model parameters from satellite "
                      << d_satellite
                      << " with CN0=" << std::setprecision(2) << cn0 << std::setprecision(default_precision)
                      << " dB-Hz" << TEXT_RESET << std::endl;
        }
    if (d_nav.have_new_iono() == true)
        {
            // get object for this SV (mandatory)
            const std::shared_ptr<Beidou_Dnav_Iono> tmp_obj =
                std::make_shared<Beidou_Dnav_Iono>(d_nav.get_iono());
            this->message_port_pub(pmt::mp("telemetry"), pmt::make_any(tmp_obj));
            LOG(INFO) << "BEIDOU DNAV Iono data have been received in channel" << d_channel
                      << " from satellite " << d_satellite << " with CN0=" << cn0 << " dB-Hz";
#if __cplusplus == 201103L
            const int default_precision = std::cout.precision();
#else
            const auto default_precision{std::cout.precision()};
#endif
            std::cout << TEXT_YELLOW << "New BEIDOU B3I DNAV Iono message received in channel "
                      << d_channel << ": Iono model parameters from satellite "
                      << d_satellite << " with CN0=" << std::setprecision(2) << cn0 << std::setprecision(default_precision)
                      << " dB-Hz" << TEXT_RESET << std::endl;
        }
    if (d_nav.have_new_almanac() == true)
        {
            //            unsigned int slot_nbr = d_nav.i_alm_satellite_PRN;
            //            std::shared_ptr<Beidou_Dnav_Almanac> tmp_obj =
            //            std::make_shared<Beidou_Dnav_Almanac>(d_nav.get_almanac(slot_nbr));
            //            this->message_port_pub(pmt::mp("telemetry"),
            //            pmt::make_any(tmp_obj));
            LOG(INFO) << "BEIDOU DNAV Almanac data have been received in channel"
                      << d_channel << " from satellite " << d_satellite << " with CN0=" << cn0 << " dB-Hz";
#if __cplusplus == 201103L
            const int default_precision = std::cout.precision();
#else
            const auto default_precision{std::cout.precision()};
#endif
            std::cout << TEXT_YELLOW << "New BEIDOU B3I DNAV almanac received in channel " << d_channel
                      << " from satellite " << d_satellite
                      << " with CN0=" << std::setprecision(2) << cn0 << std::setprecision(default_precision)
                      << " dB-Hz" << TEXT_RESET << std::endl;
        }
}


void beidou_b3i_telemetry_decoder_gs::set_satellite(
    const Gnss_Satellite &satellite)
{
    uint32_t sat_prn = 0;
    d_satellite = Gnss_Satellite(satellite.get_system(), satellite.get_PRN());
    DLOG(INFO) << "Setting decoder Finite State Machine to satellite "
               << d_satellite;
    DLOG(INFO) << "Navigation Satellite set to " << d_satellite;

    // Update satellite information for DNAV decoder
    sat_prn = d_satellite.get_PRN();
    d_nav.set_satellite_PRN(sat_prn);
    d_nav.set_signal_type(5);  // BDS: data source (0:unknown,1:B1I,2:B1Q,3:B2I,4:B2Q,5:B3I,6:B3Q)

    // Update tel dec parameters for D2 NAV Messages
    if ((sat_prn > 0 && sat_prn < 6) || sat_prn > 58)
        {
            d_symbols_per_preamble = BEIDOU_DNAV_PREAMBLE_LENGTH_SYMBOLS;
            d_samples_per_preamble = BEIDOU_DNAV_PREAMBLE_LENGTH_SYMBOLS;
            d_preamble_period_samples = BEIDOU_DNAV_PREAMBLE_PERIOD_SYMBOLS;

            // Setting samples of preamble code
            for (int32_t i = 0; i < d_symbols_per_preamble; i++)
                {
                    if (BEIDOU_DNAV_PREAMBLE[i] == '1')
                        {
                            d_preamble_samples[i] = 1;
                        }
                    else
                        {
                            d_preamble_samples[i] = -1;
                        }
                }
            d_symbol_duration_ms = BEIDOU_B3I_GEO_TELEMETRY_SYMBOLS_PER_BIT * BEIDOU_B3I_CODE_PERIOD_MS;
            d_required_symbols = BEIDOU_DNAV_SUBFRAME_SYMBOLS + d_samples_per_preamble;
            d_symbol_history.set_capacity(d_required_symbols);
        }
    else
        {
            // back to normal satellites
            d_symbol_duration_ms = BEIDOU_B3I_TELEMETRY_SYMBOLS_PER_BIT * BEIDOU_B3I_CODE_PERIOD_MS;
            d_symbols_per_preamble = BEIDOU_DNAV_PREAMBLE_LENGTH_SYMBOLS;
            d_samples_per_preamble = BEIDOU_DNAV_PREAMBLE_LENGTH_SYMBOLS;
            d_preamble_period_samples = BEIDOU_DNAV_PREAMBLE_PERIOD_SYMBOLS;

            // Setting samples of preamble code
            for (int32_t i = 0; i < d_symbols_per_preamble; i++)
                {
                    if (BEIDOU_DNAV_PREAMBLE[i] == '1')
                        {
                            d_preamble_samples[i] = 1;
                        }
                    else
                        {
                            d_preamble_samples[i] = -1;
                        }
                }

            d_required_symbols = BEIDOU_DNAV_SUBFRAME_SYMBOLS + d_samples_per_preamble;
            d_symbol_history.set_capacity(d_required_symbols);
        }
}


void beidou_b3i_telemetry_decoder_gs::set_channel(int32_t channel)
{
    d_channel = channel;
    LOG(INFO) << "Navigation channel set to " << channel;

    configure_dump_file(d_channel, d_dump, d_dump_filename, d_dump_file);
    configure_crc_stats_channel(d_channel, d_dump_crc_stats, d_Tlm_CRC_Stats);
}


void beidou_b3i_telemetry_decoder_gs::reset()
{
    d_last_valid_preamble = d_sample_counter;
    d_TOW_at_current_symbol_ms = 0;
    d_sent_tlm_failed_msg = false;
    d_flag_valid_word = false;
    DLOG(INFO) << "Beidou B3I Telemetry decoder reset for satellite " << d_satellite;
}


int beidou_b3i_telemetry_decoder_gs::general_work(
    int noutput_items __attribute__((unused)),
    gr_vector_int &ninput_items __attribute__((unused)),
    gr_vector_const_void_star &input_items, gr_vector_void_star &output_items)
{
    int32_t corr_value = 0;
    int32_t preamble_diff = 0;

    auto **out = reinterpret_cast<Gnss_Synchro **>(&output_items[0]);            // Get the output buffer pointer
    const auto **in = reinterpret_cast<const Gnss_Synchro **>(&input_items[0]);  // Get the input buffer pointer

    Gnss_Synchro current_symbol{};  // structure to save the synchronization
                                    // information and send the output object to the
                                    // next block
    // 1. Copy the current tracking output
    current_symbol = in[0][0];
    d_symbol_history.push_back(current_symbol.Prompt_I);  // add new symbol to the symbol queue
    d_sample_counter++;                                   // count for the processed samples
    consume_each(1);
    d_flag_preamble = false;

    if (d_symbol_history.size() >= d_required_symbols)
        {
            // ******* preamble correlation ********
            for (int32_t i = 0; i < d_samples_per_preamble; i++)
                {
                    if (d_symbol_history[i] < 0)  // symbols clipping
                        {
                            corr_value -= d_preamble_samples[i];
                        }
                    else
                        {
                            corr_value += d_preamble_samples[i];
                        }
                }
        }
    // ******* frame sync ******************
    if (d_stat == 0)  // no preamble information
        {
            if (abs(corr_value) >= d_samples_per_preamble)
                {
                    // Record the preamble sample stamp
                    d_preamble_index = d_sample_counter;
                    DLOG(INFO) << "Preamble detection for BEIDOU B3I SAT " << this->d_satellite;
                    // Enter into frame pre-detection status
                    d_stat = 1;
                }
        }
    else if (d_stat == 1)  // possible preamble lock
        {
            if (abs(corr_value) >= d_samples_per_preamble)
                {
                    // check preamble separation
                    preamble_diff = static_cast<int32_t>(d_sample_counter - d_preamble_index);
                    if (abs(preamble_diff - d_preamble_period_samples) == 0)
                        {
                            // try to decode frame
                            DLOG(INFO) << "Starting BeiDou DNAV frame decoding for BeiDou B3I SAT "
                                       << this->d_satellite;
                            d_preamble_index = d_sample_counter;  // record the preamble sample stamp
                            d_stat = 2;

                            // ******* SAMPLES TO SYMBOLS *******
                            if (corr_value > 0)  // normal PLL lock
                                {
                                    for (uint32_t i = 0; i < BEIDOU_DNAV_PREAMBLE_PERIOD_SYMBOLS; i++)
                                        {
                                            d_subframe_symbols[i] = d_symbol_history[i];
                                        }
                                }
                            else  // 180 deg. inverted carrier phase PLL lock
                                {
                                    for (uint32_t i = 0; i < BEIDOU_DNAV_PREAMBLE_PERIOD_SYMBOLS; i++)
                                        {
                                            d_subframe_symbols[i] = -d_symbol_history[i];
                                        }
                                }

                            // call the decoder
                            decode_subframe(d_subframe_symbols.data(), current_symbol.CN0_dB_hz);

                            if (d_nav.get_flag_CRC_test() == true)
                                {
                                    d_CRC_error_counter = 0;
                                    d_flag_preamble = true;               // valid preamble indicator (initialized to false every work())
                                    d_preamble_index = d_sample_counter;  // record the preamble sample stamp (t_P)
                                    if (!d_flag_frame_sync)
                                        {
                                            d_flag_frame_sync = true;
                                            LOG(INFO) << "Successful frame synchronization in channel " << d_channel << " for satellite " << this->d_satellite
                                                      << " at sample_counter=" << d_sample_counter;
                                        }
                                }
                            else
                                {
                                    d_CRC_error_counter++;
                                    d_preamble_index = d_sample_counter;  // record the preamble sample stamp
                                    if (d_CRC_error_counter > CRC_ERROR_LIMIT)
                                        {
                                            DLOG(INFO) << "BeiDou DNAV frame sync lost for SAT "
                                                       << this->d_satellite;
                                            d_flag_frame_sync = false;
                                            d_stat = 0;
                                            d_flag_SOW_set = false;
                                        }
                                }
                        }
                    else
                        {
                            if (preamble_diff > d_preamble_period_samples)
                                {
                                    d_stat = 0;  // start again
                                }
                            DLOG(INFO) << "Failed BeiDou DNAV frame decoding for BeiDou B3I SAT "
                                       << this->d_satellite;
                        }
                }
        }
    else if (d_stat == 2)  // preamble acquired
        {
            if (d_sample_counter == d_preamble_index + static_cast<uint64_t>(d_preamble_period_samples))
                {
                    // ******* SAMPLES TO SYMBOLS *******
                    if (corr_value > 0)  // normal PLL lock
                        {
                            for (uint32_t i = 0; i < BEIDOU_DNAV_PREAMBLE_PERIOD_SYMBOLS; i++)
                                {
                                    d_subframe_symbols[i] = d_symbol_history[i];
                                }
                        }
                    else  // 180 deg. inverted carrier phase PLL lock
                        {
                            for (uint32_t i = 0; i < BEIDOU_DNAV_PREAMBLE_PERIOD_SYMBOLS; i++)
                                {
                                    d_subframe_symbols[i] = -d_symbol_history[i];
                                }
                        }

                    // call the decoder
                    decode_subframe(d_subframe_symbols.data(), current_symbol.CN0_dB_hz);

                    if (d_nav.get_flag_CRC_test() == true)
                        {
                            d_CRC_error_counter = 0;
                            d_flag_preamble = true;               // valid preamble indicator (initialized to false every work())
                            d_preamble_index = d_sample_counter;  // record the preamble sample stamp (t_P)
                            if (!d_flag_frame_sync)
                                {
                                    d_flag_frame_sync = true;
                                    DLOG(INFO) << "BeiDou DNAV frame sync found for SAT "
                                               << this->d_satellite;
                                }
                        }
                    else
                        {
                            d_CRC_error_counter++;
                            d_preamble_index = d_sample_counter;  // record the preamble sample stamp
                            if (d_CRC_error_counter > CRC_ERROR_LIMIT)
                                {
                                    DLOG(INFO) << "BeiDou DNAV frame sync lost for SAT "
                                               << this->d_satellite;
                                    d_flag_frame_sync = false;
                                    d_stat = 0;
                                    d_flag_SOW_set = false;
                                }
                        }
                }
        }
    // UPDATE GNSS SYNCHRO DATA
    // 2. Add the telemetry decoder information
    if (this->d_flag_preamble == true && d_nav.get_flag_new_SOW_available() == true)
        // update TOW at the preamble instant
        {
            // Reporting sow as gps time of week
            d_TOW_at_Preamble_ms = static_cast<uint32_t>((d_nav.get_SOW() + BEIDOU_DNAV_BDT2GPST_LEAP_SEC_OFFSET) * 1000.0);
            // check TOW update consistency
            const uint32_t last_d_TOW_at_current_symbol_ms = d_TOW_at_current_symbol_ms;
            // compute new TOW
            d_TOW_at_current_symbol_ms = d_TOW_at_Preamble_ms + d_required_symbols * d_symbol_duration_ms;
            d_flag_SOW_set = true;
            d_nav.set_flag_new_SOW_available(false);

            if (last_d_TOW_at_current_symbol_ms != 0 && abs(static_cast<int64_t>(d_TOW_at_current_symbol_ms) - int64_t(last_d_TOW_at_current_symbol_ms)) > static_cast<int64_t>(d_symbol_duration_ms))
                {
                    LOG(INFO) << "Warning: BEIDOU B3I TOW update in ch " << d_channel
                              << " does not match the TLM TOW counter " << static_cast<int64_t>(d_TOW_at_current_symbol_ms) - int64_t(last_d_TOW_at_current_symbol_ms) << " ms \n";

                    d_TOW_at_current_symbol_ms = 0;
                    d_flag_valid_word = false;
                }
            else
                {
                    d_last_valid_preamble = d_sample_counter;
                    d_flag_valid_word = true;
                }

            if (d_enable_navdata_monitor && !d_nav_msg_packet.nav_message.empty())
                {
                    d_nav_msg_packet.prn = static_cast<int32_t>(current_symbol.PRN);
                    d_nav_msg_packet.tow_at_current_symbol_ms = static_cast<int32_t>(d_TOW_at_current_symbol_ms);
                    const std::shared_ptr<Nav_Message_Packet> tmp_obj = std::make_shared<Nav_Message_Packet>(d_nav_msg_packet);
                    this->message_port_pub(pmt::mp("Nav_msg_from_TLM"), pmt::make_any(tmp_obj));
                    d_nav_msg_packet.nav_message = "";
                }
        }
    else
        {
            if (d_flag_valid_word)
                {
                    d_TOW_at_current_symbol_ms += d_symbol_duration_ms;
                    if (current_symbol.Flag_valid_symbol_output == false)
                        {
                            d_flag_valid_word = false;
                        }
                }
        }

    if (d_flag_valid_word == true)
        {
            current_symbol.TOW_at_current_symbol_ms = d_TOW_at_current_symbol_ms;
            current_symbol.Flag_valid_word = d_flag_valid_word;

            if (d_dump == true)
                {
                    // MULTIPLEXED FILE RECORDING - Record results to file
                    try
                        {
                            double tmp_double;
                            uint64_t tmp_ulong_int;
                            int32_t tmp_int;
                            tmp_double = static_cast<double>(d_TOW_at_current_symbol_ms) / 1000.0;
                            d_dump_file.write(reinterpret_cast<char *>(&tmp_double), sizeof(double));
                            tmp_ulong_int = current_symbol.Tracking_sample_counter;
                            d_dump_file.write(reinterpret_cast<char *>(&tmp_ulong_int), sizeof(uint64_t));
                            tmp_double = static_cast<double>(d_TOW_at_Preamble_ms) / 1000.0;
                            d_dump_file.write(reinterpret_cast<char *>(&tmp_double), sizeof(double));
                            tmp_int = (current_symbol.Prompt_I > 0.0 ? 1 : -1);
                            d_dump_file.write(reinterpret_cast<char *>(&tmp_int), sizeof(int32_t));
                            tmp_int = static_cast<int32_t>(current_symbol.PRN);
                            d_dump_file.write(reinterpret_cast<char *>(&tmp_int), sizeof(int32_t));
                        }
                    catch (const std::ofstream::failure &e)
                        {
                            LOG(WARNING) << "Exception writing Telemetry GPS L5 dump file " << e.what();
                        }
                }

            // SEND TOW TO THE TRACKING BLOCK
            if (d_tow_to_trk)
                {
                    const std::shared_ptr<TOW_to_trk> tmp_tow_obj = std::make_shared<TOW_to_trk>(TOW_to_trk(
                        std::string(current_symbol.Signal),
                        d_channel,
                        d_TOW_at_current_symbol_ms,
                        current_symbol.Tracking_sample_counter,
                        d_nav.get_ephemeris().WN, d_satellite.get_PRN()));
                    this->message_port_pub(pmt::mp("telemetry_to_trk"), pmt::make_any(tmp_tow_obj));
                }

            // 3. Make the output (move the object contents to the GNURadio reserved memory)
            *out[0] = std::move(current_symbol);
            return 1;
        }
    return 0;
}
