diff --git a/CMakeLists.txt b/CMakeLists.txt index ffa66cf5b..0bb716ad9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -57,6 +57,8 @@ option(ENABLE_ARRAY "Enable the use of CTTC's antenna array front-end as signal option(ENABLE_ZMQ "Enable GNU Radio ZeroMQ Messaging, requires gr-zeromq" ON) +option(ENABLE_ION "Enable ION GNSS-SDR Metadata Standard signal source" ON) + # Performance analysis tools option(ENABLE_GPERFTOOLS "Enable linking to Gperftools libraries (tcmalloc and profiler)" OFF) @@ -1422,6 +1424,19 @@ else() endif() +################################################################################ +# ION GNSS-SDR Metadata Standard +################################################################################ +include(FetchContent) +FetchContent_Declare( + gnss_metadata_standard + GIT_REPOSITORY https://github.com/IonMetadataWorkingGroup/GNSS-Metadata-Standard + GIT_TAG master + SOURCE_DIR ${GNSSSDR_BINARY_DIR}/thirdparty/gnss-metadata-standard + CMAKE_ARGS -DABSL_PROPAGATE_CXX_STD=ON -ABSL_BUILD_TESTING=OFF -DCMAKE_INSTALL_PREFIX=${GNSSSDR_BINARY_DIR}/gnss-metadata-standard ${ABSEIL_TOOLCHAIN_FILE} + BINARY_DIR ${GNSSSDR_BINARY_DIR}/gnss-metadata-standard +) +FetchContent_MakeAvailable(gnss_metadata_standard) ################################################################################ # Abseil C++ - https://abseil.io/docs/cpp/ diff --git a/src/algorithms/signal_source/adapters/CMakeLists.txt b/src/algorithms/signal_source/adapters/CMakeLists.txt index 0aa745101..54d577c53 100644 --- a/src/algorithms/signal_source/adapters/CMakeLists.txt +++ b/src/algorithms/signal_source/adapters/CMakeLists.txt @@ -119,6 +119,7 @@ set(SIGNAL_SOURCE_ADAPTER_SOURCES two_bit_packed_file_signal_source.cc four_bit_cpx_file_signal_source.cc file_timestamp_signal_source.cc + ion_gnss_ms_signal_source.cc ${OPT_DRIVER_SOURCES} ) @@ -138,6 +139,7 @@ set(SIGNAL_SOURCE_ADAPTER_HEADERS two_bit_packed_file_signal_source.h four_bit_cpx_file_signal_source.h file_timestamp_signal_source.h + ion_gnss_ms_signal_source.h ${OPT_DRIVER_HEADERS} ) diff --git a/src/algorithms/signal_source/adapters/ion_gnss_ms_signal_source.cc b/src/algorithms/signal_source/adapters/ion_gnss_ms_signal_source.cc new file mode 100644 index 000000000..fe81e4556 --- /dev/null +++ b/src/algorithms/signal_source/adapters/ion_gnss_ms_signal_source.cc @@ -0,0 +1,119 @@ +/*! + * \file file_timestamp_signal_source.cc + * \brief This class reads samples stored in a file and generate stream tags + * with its timestamp information stored in separated file + * \author Javier Arribas, jarribas(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) + * SPDX-License-Identifier: GPL-3.0-or-later + * + * ----------------------------------------------------------------------------- + */ + +#include "ion_gnss_ms_signal_source.h" +#include "gnss_sdr_flags.h" +#include "gnss_sdr_string_literals.h" +#include +#include +#include + +#if USE_GLOG_AND_GFLAGS +#include +#else +#include +#endif + +using namespace std::string_literals; + +std::vector parse_comma_list(const std::string& str) +{ + std::vector list{}; + std::size_t prev_comma_at{0}; + + while (prev_comma_at < str.size()) + { + std::size_t comma_at = str.find_first_of(',', prev_comma_at); + if (comma_at == std::string::npos) + { + comma_at = str.size(); + } + list.emplace_back(str.substr(prev_comma_at, (comma_at - prev_comma_at))); + prev_comma_at = comma_at + 1; + } + + return list; +} + +IONMetadataStandardSignalSource::IONMetadataStandardSignalSource(const ConfigurationInterface* configuration, + const std::string& role, + unsigned int in_streams, + unsigned int out_streams, + Concurrent_Queue* queue) + : SignalSourceBase(configuration, role, "ION_Metadata_Standard_Signal_Source"s), + metadata_file_(configuration->property(role + ".metadata_filename"s, "../data/example_capture_metadata.sdrx"s)), + stream_ids_(parse_comma_list(configuration->property(role + ".streams"s, ""s))), + metadata_(metadata_file_), + timestamp_clock_offset_ms_(configuration->property(role + ".timestamp_clock_offset_ms"s, 0.0)) +{ + if (in_streams > 0) + { + LOG(ERROR) << "A signal source does not have an input stream"; + } + + sources_ = metadata_.make_stream_sources(stream_ids_); + + for (const auto& source : sources_) + { + for (int i = 0; i < source->output_stream_count(); ++i) + { + copy_blocks_.push_back(gr::blocks::copy::make(source->output_stream_item_size(i))); + } + } +} + + +void IONMetadataStandardSignalSource::connect(gr::top_block_sptr top_block) +{ + std::size_t cumulative_index = 0; + for (const auto& source : sources_) + { + for (int i = 0; i < source->output_stream_count(); ++i, ++cumulative_index) + { + top_block->connect(source, i, copy_blocks_[cumulative_index], 0); + } + } +} + +void IONMetadataStandardSignalSource::disconnect(gr::top_block_sptr top_block) +{ + std::size_t cumulative_index = 0; + for (const auto& source : sources_) + { + for (int i = 0; i < source->output_stream_count(); ++i, ++cumulative_index) + { + top_block->disconnect(source, i, copy_blocks_[cumulative_index], 0); + } + } +} + +gr::basic_block_sptr IONMetadataStandardSignalSource::get_left_block() +{ + LOG(WARNING) << "Trying to get signal source left block."; + // return gr_basic_block_sptr(); + return IONMetadataStdFileSource::sptr(); +} + +gr::basic_block_sptr IONMetadataStandardSignalSource::get_right_block() +{ + return get_right_block(0); +} + +gr::basic_block_sptr IONMetadataStandardSignalSource::get_right_block(int RF_channel) +{ + return copy_blocks_[RF_channel]; +} diff --git a/src/algorithms/signal_source/adapters/ion_gnss_ms_signal_source.h b/src/algorithms/signal_source/adapters/ion_gnss_ms_signal_source.h new file mode 100644 index 000000000..ed84cab5c --- /dev/null +++ b/src/algorithms/signal_source/adapters/ion_gnss_ms_signal_source.h @@ -0,0 +1,78 @@ +/*! + * \file file_timestamp_signal_source.h + * \brief This class reads samples stored in a file and generate stream tags + * with its timestamp information stored in separated file + * \author Javier Arribas, jarribas(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) + * SPDX-License-Identifier: GPL-3.0-or-later + * + * ----------------------------------------------------------------------------- + */ + + +#ifndef GNSS_SDR_ION_METADATA_STANDARD_SIGNAL_SOURCE_H +#define GNSS_SDR_ION_METADATA_STANDARD_SIGNAL_SOURCE_H + +#include "configuration_interface.h" +#include "file_source_base.h" +#include "gnss_sdr_timestamp.h" +#include "ion_gnss_sdr_metadata_standard.h" +#include + +/** \addtogroup Signal_Source + * \{ */ +/** \addtogroup Signal_Source_adapters + * \{ */ + +/*! + * \brief Class that reads signals samples from a file + * and adapts it to a SignalSourceInterface + */ +class IONMetadataStandardSignalSource : public SignalSourceBase +{ +public: + IONMetadataStandardSignalSource(const ConfigurationInterface* configuration, const std::string& role, + unsigned int in_streams, unsigned int out_streams, + Concurrent_Queue* queue); + + ~IONMetadataStandardSignalSource() override = default; + +protected: + // std::tuple itemTypeToSize() override; + // double packetsPerSample() const override; + void connect(gr::top_block_sptr top_block) override; + void disconnect(gr::top_block_sptr top_block) override; + + gr::basic_block_sptr get_left_block() override; + gr::basic_block_sptr get_right_block() override; + gr::basic_block_sptr get_right_block(int RF_channel) override; + + inline size_t item_size() override + { + return (*sources_.begin())->output_stream_item_size(0); + } +private: + std::string metadata_file_; + std::vector stream_ids_; + std::vector sources_; + std::vector> copy_blocks_; + GnssMetadataHandler metadata_; + + gnss_shared_ptr timestamp_block_; + std::string timestamp_file_; + double timestamp_clock_offset_ms_; + + uint32_t in_streams_; + uint32_t out_streams_; +}; + + +/** \} */ +/** \} */ +#endif // GNSS_SDR_ION_METADATA_STANDARD_SIGNAL_SOURCE_H diff --git a/src/algorithms/signal_source/libs/CMakeLists.txt b/src/algorithms/signal_source/libs/CMakeLists.txt index fd5c664d5..f0b2c09fd 100644 --- a/src/algorithms/signal_source/libs/CMakeLists.txt +++ b/src/algorithms/signal_source/libs/CMakeLists.txt @@ -7,6 +7,7 @@ set(OPT_SIGNAL_SOURCE_LIB_SOURCES "") set(OPT_SIGNAL_SOURCE_LIB_HEADERS "") + if(ENABLE_FMCOMMS2 OR ENABLE_AD9361) set(OPT_SIGNAL_SOURCE_LIB_SOURCES ${OPT_SIGNAL_SOURCE_LIB_SOURCES} ad9361_manager.cc) set(OPT_SIGNAL_SOURCE_LIB_HEADERS ${OPT_SIGNAL_SOURCE_LIB_HEADERS} ad9361_manager.h) @@ -44,6 +45,11 @@ if(ENABLE_PLUTOSDR) set(OPT_SIGNAL_SOURCE_LIB_HEADERS ${OPT_SIGNAL_SOURCE_LIB_HEADERS} ppstcprx.h) endif() +if(ENABLE_ION) + set(OPT_SIGNAL_SOURCE_LIB_SOURCES ${OPT_SIGNAL_SOURCE_LIB_SOURCES} ion_gnss_sdr_metadata_standard.cc) + set(OPT_SIGNAL_SOURCE_LIB_HEADERS ${OPT_SIGNAL_SOURCE_LIB_HEADERS} ion_gnss_sdr_metadata_standard.h) +endif() + set(SIGNAL_SOURCE_LIB_SOURCES rtl_tcp_commands.cc @@ -139,6 +145,11 @@ if(ENABLE_CLANG_TIDY) endif() endif() +if(ENABLE_ION) + target_include_directories(signal_source_libs PUBLIC ${GNSSSDR_BINARY_DIR}/thirdparty/gnss-metadata-standard/source/api/inc) + target_link_libraries(signal_source_libs PUBLIC api xml) +endif() + set_property(TARGET signal_source_libs APPEND PROPERTY INTERFACE_INCLUDE_DIRECTORIES $ ) diff --git a/src/algorithms/signal_source/libs/ion_gnss_sdr_metadata_standard.cc b/src/algorithms/signal_source/libs/ion_gnss_sdr_metadata_standard.cc new file mode 100644 index 000000000..c31ec3d1d --- /dev/null +++ b/src/algorithms/signal_source/libs/ion_gnss_sdr_metadata_standard.cc @@ -0,0 +1,304 @@ +// +// Created by castle on 6/24/24. +// + +#include "ion_gnss_sdr_metadata_standard.h" + +#if USE_GLOG_AND_GFLAGS +#include +#else +#include +#endif + +GnssMetadataHandler::GnssMetadataHandler(const std::string& metadata_filepath) + : metadata_filepath_(metadata_filepath) +{ + load_metadata(); +} + +const std::string& GnssMetadataHandler::metadata_filepath() const +{ + return metadata_filepath_; +} + +void GnssMetadataHandler::load_metadata() +{ + try + { + GnssMetadata::XmlProcessor xml_proc; + if (!xml_proc.Load(metadata_filepath_.c_str(), false, metadata_)) + { + LOG(ERROR) << "Could not load XML metadata file:"; + } + } + catch (GnssMetadata::ApiException& e) + { + LOG(ERROR) << "API Exception while loadind XML metadata file: " << e.what(); + } + catch (std::exception& e) + { + LOG(ERROR) << "Exception while loading XML metadata file: " << e.what(); + } +} + +std::vector GnssMetadataHandler::make_stream_sources(const std::vector& stream_ids) const +{ + std::vector sources{}; + for (const auto& file : metadata_.Files()) + { + for (const auto& block : file.Lane().Blocks()) + { + for (const auto& chunk : block.Chunks()) + { + for (const auto& lump : chunk.Lumps()) + { + for (const auto& stream : lump.Streams()) + { + if (std::ranges::any_of(stream_ids.begin(), stream_ids.end(), [&](const std::string& it) { + return stream.Id() == it; + })) + { + auto source = gnss_make_shared( + file, + block, + stream_ids); + + sources.push_back(source); + + // This file source will take care of any other matching streams in this block + // We can skip the rest of this block + goto next_block; + } + } + } + } + next_block: + } + } + + return sources; +} + + +IONMetadataStdFileSource::IONMetadataStdFileSource( + const GnssMetadata::File& file, + const GnssMetadata::Block& block, + const std::vector& stream_ids) + : gr::sync_block( + "ion_metadata_standard_source", + gr::io_signature::make(0, 0, 0), + make_output_signature(block)), + file_metadata_(file), + block_metadata_(block) +{ + fd_ = std::fopen(file.Url().Value().c_str(), "rb"); + std::size_t block_offset = file.Offset(); + std::fseek(fd_, file.Offset() + block_offset + block.SizeHeader(), SEEK_SET); + + std::size_t output_stream_offset = 0; + for (const auto& chunk : block.Chunks()) + { + chunk_data_.emplace_back(std::make_shared(chunk, stream_ids, output_stream_offset)); + const std::size_t out_count = chunk_data_.back()->output_stream_count(); + output_stream_offset += out_count; + for (std::size_t i = 0; i < out_count; ++i) + { + output_stream_item_sizes_.push_back(chunk_data_.back()->output_stream_item_size(i)); + } + } + output_stream_count_ = output_stream_offset; +} + +IONMetadataStdFileSource::~IONMetadataStdFileSource() +{ + std::fclose(fd_); +} + +int IONMetadataStdFileSource::work( + int noutput_items, + gr_vector_const_void_star& input_items, + gr_vector_void_star& output_items) +{ + for (int i = 0; i < noutput_items; ++i) + { + read_chunk_pattern(output_items); + } + return WORK_CALLED_PRODUCE; +} + +std::size_t IONMetadataStdFileSource::output_stream_count() const +{ + return output_stream_count_; +} + +std::size_t IONMetadataStdFileSource::output_stream_item_size(std::size_t stream_index) const +{ + return output_stream_item_sizes_[stream_index]; +} + + +void IONMetadataStdFileSource::read_chunk_pattern(gr_vector_void_star& output_items) +{ + gr_vector_void_star chunk_outputs = output_items; + for (auto& c : chunk_data_) + { + c->read_from_file(fd_); + c->write_to_output(output_items, [this](int output, int nitems) { + produce(output, nitems); + }); + } +} + +gr::io_signature::sptr IONMetadataStdFileSource::make_output_signature(const GnssMetadata::Block& block) +{ + int nstreams = 0; + std::vector item_sizes{}; + + for (const auto& chunk : block.Chunks()) + { + for (const auto& lump : chunk.Lumps()) + { + for (const auto& stream : lump.Streams()) + { + ++nstreams; + item_sizes.emplace_back(stream.RateFactor() * stream.Quantization()); + } + } + } + + return gr::io_signature::make( + nstreams, + nstreams, + item_sizes); +} + + +chunk_data_t::chunk_data_t(const GnssMetadata::Chunk& chunk, const std::vector& stream_ids, std::size_t output_stream_offset) + : chunk_(chunk), + sizeword_(chunk_.SizeWord()), + countwords_(chunk_.CountWords()) +{ + switch (sizeword_) + { + case 1: + buffer_ = new uint8_t[countwords_]; + break; + case 2: + buffer_ = new uint16_t[countwords_]; + break; + case 4: + buffer_ = new uint32_t[countwords_]; + break; + case 8: + buffer_ = new uint64_t[countwords_]; + break; + default: + LOG(ERROR) << "Unknown word size: " << std::to_string(sizeword_); + break; + } + + + const std::size_t total_bitsize = sizeword_ * countwords_ * 8; + std::size_t used_bitsize = 0; + std::size_t output_streams = 0; + for (const auto& lump : chunk.Lumps()) + { + for (const auto& stream : lump.Streams()) + { + used_bitsize += stream.Packedbits(); + + if (std::ranges::any_of(stream_ids.begin(), stream_ids.end(), [&](const std::string& it) { + return stream.Id() == it; + })) + { + streams_.emplace_back(lump, stream, output_streams + output_stream_offset); + ++output_streams; + std::size_t sample_bitsize = stream.Packedbits() / stream.RateFactor(); + if (sample_bitsize <= 8) + { + output_stream_item_size_.push_back(1); + } + else if (sample_bitsize <= 16) + { + output_stream_item_size_.push_back(2); + } + else if (sample_bitsize <= 32) + { + output_stream_item_size_.push_back(4); + } + else if (sample_bitsize <= 64) + { + output_stream_item_size_.push_back(8); + } + else + { + // This shouldn't happen + output_stream_item_size_.push_back(1); + } + } + else + { + streams_.emplace_back(lump, stream, -1); + } + } + } + + output_stream_count_ = output_streams; + padding_bitsize_ = total_bitsize - used_bitsize; +} +chunk_data_t::~chunk_data_t() +{ + switch (sizeword_) + { + case 1: + delete[] static_cast(buffer_); + break; + case 2: + delete[] static_cast(buffer_); + break; + case 4: + delete[] static_cast(buffer_); + break; + case 8: + delete[] static_cast(buffer_); + break; + default: + break; + } +} + +void chunk_data_t::read_from_file(FILE* fd) +{ + std::fread(buffer_, sizeword_, countwords_, fd); +} + +void chunk_data_t::write_to_output(gr_vector_void_star& outputs, const std::function& produce) +{ + switch (sizeword_) + { + case 1: + unpack_words(outputs, produce); + break; + case 2: + unpack_words(outputs, produce); + break; + case 4: + unpack_words(outputs, produce); + break; + case 8: + unpack_words(outputs, produce); + break; + default: + break; + } +} + +std::size_t chunk_data_t::output_stream_count() const +{ + return output_stream_count_; +} + +std::size_t chunk_data_t::output_stream_item_size(std::size_t stream_index) const +{ + return output_stream_item_size_[stream_index]; +} diff --git a/src/algorithms/signal_source/libs/ion_gnss_sdr_metadata_standard.h b/src/algorithms/signal_source/libs/ion_gnss_sdr_metadata_standard.h new file mode 100644 index 000000000..badf834c1 --- /dev/null +++ b/src/algorithms/signal_source/libs/ion_gnss_sdr_metadata_standard.h @@ -0,0 +1,319 @@ +// +// Created by castle on 6/24/24. +// + +#ifndef GNSS_SDR_ION_GNSS_SDR_METADATA_STANDARD_H +#define GNSS_SDR_ION_GNSS_SDR_METADATA_STANDARD_H + +#include "GnssMetadata.h" +#include "gnss_block_interface.h" +#include +#include + +class chunk_data_t +{ +public: + chunk_data_t(const GnssMetadata::Chunk& chunk, const std::vector& stream_ids, std::size_t output_stream_offset); + + ~chunk_data_t(); + + chunk_data_t(const chunk_data_t& rhl) = delete; + chunk_data_t& operator=(const chunk_data_t& rhl) = delete; + + chunk_data_t(chunk_data_t&& rhl) = delete; + chunk_data_t& operator=(chunk_data_t&& rhl) = delete; + + void read_from_file(FILE* fd); + + void write_to_output(gr_vector_void_star& outputs, const std::function& produce); + + std::size_t output_stream_count() const; + std::size_t output_stream_item_size(std::size_t stream_index) const; + +private: + template + struct unpacking_context_t + { + WT* iterator_; + WT current_word_; + uint8_t bitshift_ = 0; + }; + + template + void unpack_words(gr_vector_void_star& outputs, const std::function& produce) + { + WT* data = static_cast(buffer_); + // TODO - Swap endiannes if needed + + unpacking_context_t ctx{}; + if (chunk_.Shift() == GnssMetadata::Chunk::Left) + { + ctx.iterator_ = data; + } + else if (chunk_.Shift() == GnssMetadata::Chunk::Right) + { + ctx.iterator_ = &data[countwords_]; + } + advance_word(ctx); // Initializes ctx.current_word_ + + // Head padding + if (padding_bitsize_ > 0 && chunk_.Padding() == GnssMetadata::Chunk::Head) + { + shift_padding(ctx, padding_bitsize_); + } + + // Samples + for (const auto& [lump, stream, output_index] : streams_) + { + if (output_index == -1) + { + skip_stream(ctx, lump, stream); + } + else + { + produce(output_index, write_stream_samples(ctx, lump, stream, outputs[output_index])); + } + } + } + + template + void skip_stream( + unpacking_context_t& ctx, + const GnssMetadata::Lump& lump, + const GnssMetadata::IonStream& stream) + { + shift_padding(ctx, stream.Packedbits()); + } + + template + std::size_t write_stream_samples( + unpacking_context_t& ctx, + const GnssMetadata::Lump& lump, + const GnssMetadata::IonStream& stream, + void*& out) + { + std::size_t sample_bitsize = stream.Packedbits() / stream.RateFactor(); + std::size_t sample_count = stream.RateFactor(); + if (sample_bitsize <= 8) + { + write_n_samples(ctx, lump.Shift(), sample_bitsize, sample_count, out); + } + else if (sample_bitsize <= 16) + { + write_n_samples(ctx, lump.Shift(), sample_bitsize, sample_count, out); + } + else if (sample_bitsize <= 32) + { + write_n_samples(ctx, lump.Shift(), sample_bitsize, sample_count, out); + } + else if (sample_bitsize <= 64) + { + write_n_samples(ctx, lump.Shift(), sample_bitsize, sample_count, out); + } + + return sample_count; + } + + template + void write_n_samples( + unpacking_context_t& ctx, + GnssMetadata::Lump::LumpShift lump_shift, + uint8_t sample_bitsize, + std::size_t sample_count, + void*& out) + { + if (lump_shift == GnssMetadata::Lump::shiftLeft) + { + auto* sample = static_cast(out); + for (int i = 0; i < sample_count; ++i) + { + shift_sample(ctx, sample_bitsize, sample); + ++sample; + } + } + else if (lump_shift == GnssMetadata::Lump::shiftRight) + { + auto* sample = static_cast(out); + sample += sample_count; + for (int i = 0; i < sample_count; ++i) + { + shift_sample(ctx, sample_bitsize, sample); + --sample; + } + } + } + + template + void shift_sample(unpacking_context_t& ctx, uint8_t sample_bitsize, OT* output, uint8_t output_bit_offset = 0) + { + const uint8_t word_bitsize = sizeword_ * 8; + + if ((sample_bitsize + (ctx.bitshift_ % word_bitsize)) > word_bitsize) + { + uint8_t bits_shifted = word_bitsize - (ctx.bitshift_ % word_bitsize); + + if (chunk_.Shift() == GnssMetadata::Chunk::Left) + { + WT mask = ~((1 << (word_bitsize - bits_shifted)) - 1); + *output |= ((ctx.current_word_ & mask) >> output_bit_offset); + ctx.current_word_ <<= bits_shifted; + } + else if (chunk_.Shift() == GnssMetadata::Chunk::Right) + { + WT mask = ((1 << (bits_shifted)) - 1); + *output |= (ctx.current_word_ & mask) << output_bit_offset; + // TODO - reverse bit order of sample? maybe? + ctx.current_word_ >>= bits_shifted; + } + + advance_word(ctx); + ctx.bitshift_ += bits_shifted; + shift_sample(ctx, sample_bitsize - bits_shifted, output, bits_shifted); + } + else + { + if (chunk_.Shift() == GnssMetadata::Chunk::Left) + { + WT mask = ~((1 << (word_bitsize - sample_bitsize)) - 1); + *output |= (ctx.current_word_ & mask) >> output_bit_offset; + ctx.current_word_ <<= sample_bitsize; + } + else if (chunk_.Shift() == GnssMetadata::Chunk::Right) + { + WT mask = ((1 << (sample_bitsize)) - 1); + *output |= (ctx.current_word_ & mask) << output_bit_offset; + // TODO - reverse bit order of sample? maybe? + ctx.current_word_ >>= sample_bitsize; + } + + ctx.bitshift_ += sample_bitsize; + } + } + + template + void shift_padding(unpacking_context_t& ctx, uint8_t n_bits) + { + const uint8_t word_bitsize = sizeword_ * 8; + + if ((n_bits + (ctx.bitshift_ % word_bitsize)) > word_bitsize) + { + uint8_t bits_shifted = word_bitsize - (ctx.bitshift_ % word_bitsize); + + if (chunk_.Shift() == GnssMetadata::Chunk::Left) + { + ctx.current_word_ <<= bits_shifted; + } + else if (chunk_.Shift() == GnssMetadata::Chunk::Right) + { + ctx.current_word_ >>= bits_shifted; + } + + advance_word(ctx); + ctx.bitshift_ += bits_shifted; + shift_padding(ctx, n_bits - bits_shifted); + } + else + { + if (chunk_.Shift() == GnssMetadata::Chunk::Left) + { + ctx.current_word_ <<= n_bits; + } + else if (chunk_.Shift() == GnssMetadata::Chunk::Right) + { + ctx.current_word_ >>= n_bits; + } + + ctx.bitshift_ += n_bits; + } + } + + template + void advance_word(unpacking_context_t& ctx) + { + WT word = *ctx.iterator_; + if (chunk_.Shift() == GnssMetadata::Chunk::Left) + { + ++ctx.iterator_; + } + else if (chunk_.Shift() == GnssMetadata::Chunk::Right) + { + --ctx.iterator_; + } + + ctx.current_word_ = word; + } + +private: + const GnssMetadata::Chunk& chunk_; + uint8_t sizeword_; + uint8_t countwords_; + uint8_t padding_bitsize_; + std::size_t output_stream_count_; + std::vector output_stream_item_size_; + + struct stream_metadata_t + { + const GnssMetadata::Lump& lump; + const GnssMetadata::IonStream& stream; + int output_index = -1; + }; + std::vector streams_; + + void* buffer_; +}; + +class IONMetadataStdFileSource : public gr::sync_block +{ +public: + using sptr = gnss_shared_ptr; + + IONMetadataStdFileSource( + const GnssMetadata::File& file, + const GnssMetadata::Block& block, + const std::vector& stream_ids); + + ~IONMetadataStdFileSource() override; + + int work( + int noutput_items, + gr_vector_const_void_star& input_items, + gr_vector_void_star& output_items) override; + + std::size_t output_stream_count() const; + std::size_t output_stream_item_size(std::size_t stream_index) const; + +private: + void read_chunk_pattern(gr_vector_void_star& output_items); + +private: + static gr::io_signature::sptr make_output_signature(const GnssMetadata::Block& block); + +private: + const GnssMetadata::File& file_metadata_; + const GnssMetadata::Block& block_metadata_; + FILE* fd_; + std::size_t output_stream_count_; + std::vector output_stream_item_sizes_; + std::vector> chunk_data_; +}; + +class GnssMetadataHandler +{ +public: + explicit GnssMetadataHandler(const std::string& metadata_filepath); + + std::vector make_stream_sources(const std::vector& stream_ids) const; + +public: // Getters + const std::string& metadata_filepath() const; + +private: + void load_metadata(); + +private: + std::string metadata_filepath_; + GnssMetadata::Metadata metadata_; +}; + + +#endif // GNSS_SDR_ION_GNSS_SDR_METADATA_STANDARD_H diff --git a/src/core/receiver/gnss_block_factory.cc b/src/core/receiver/gnss_block_factory.cc index ce77442ee..69406a711 100644 --- a/src/core/receiver/gnss_block_factory.cc +++ b/src/core/receiver/gnss_block_factory.cc @@ -113,6 +113,7 @@ #include "tracking_interface.h" #include "two_bit_cpx_file_signal_source.h" #include "two_bit_packed_file_signal_source.h" +#include "ion_gnss_ms_signal_source.h" #include // for exit #include // for exception #include // for cerr @@ -759,6 +760,12 @@ std::unique_ptr GNSSBlockFactory::GetBlock( block = std::move(block_); } #endif + else if (implementation == "ION_Metadata_Standard_Signal_Source") + { + std::unique_ptr block_ = std::make_unique(configuration, role, in_streams, + out_streams, queue); + block = std::move(block_); + } #if RAW_ARRAY_DRIVER else if (implementation == "Raw_Array_Signal_Source")