From c5ed04572f6cb6ced8ad80b9bdb417e855b8f363 Mon Sep 17 00:00:00 2001 From: Piotr Krysik Date: Tue, 16 Jul 2019 16:17:43 +0200 Subject: receiver/time_sample_ref: SDR hardware time tracking Gr-gsm's receiver needs a stream of samples with sampling frequency that is integer multiply of gsm symbol rate. Many hardware receivers don't support such rates, and even if they do there might be error in their time source. Sample rate is adapted and corrected with use of a resampler. Moreover corrections are applied in the runtime so resampler's resample rate is changed dynamically. Those changes need to be tracked in order to be able to track time of the hardware after resampling. Hardware time tracking is needed to enable triggering of events in the hardware (i.e. frequency changes, burst transmissions). Changes were made in time_sample_ref and receiver in order to enable SDR hardware time tracking. Change-Id: Ide8149a5fc6c22700d3a4542115306a4dc682ff2 --- lib/receiver/receiver_impl.cc | 89 ++++++++++++++++++++++------------------- lib/receiver/receiver_impl.h | 15 ++++++- lib/receiver/time_sample_ref.cc | 29 ++++---------- lib/receiver/time_sample_ref.h | 77 ++++++++++++++++++++++++++++++----- 4 files changed, 137 insertions(+), 73 deletions(-) diff --git a/lib/receiver/receiver_impl.cc b/lib/receiver/receiver_impl.cc index 4570b96..ac60bfd 100644 --- a/lib/receiver/receiver_impl.cc +++ b/lib/receiver/receiver_impl.cc @@ -87,7 +87,7 @@ namespace gr gr::io_signature::make(0, 0, 0)), d_samples_consumed(0), d_rx_time_received(false), - d_time_samp_ref(GSM_SYMBOL_RATE * osr), + d_time_samp_ref(osr*GSM_SYMBOL_RATE, resamp_rate), d_OSR(osr), d_process_uplink(process_uplink), d_chan_imp_length(CHAN_IMP_RESP_LENGTH), @@ -152,34 +152,16 @@ namespace gr gr_vector_void_star &output_items) { gr_complex *input = (gr_complex *) input_items[0]; - uint64_t start = nitems_read(0); - uint64_t stop = start + noutput_items; - d_freq_offset_tag_in_fcch = false; - -#if 0 - /* FIXME: jak zrobić to rzutowanie poprawnie */ - std::vector iii = - (std::vector) input_items; -#endif - /* Time synchronization loop */ - float current_time = - static_cast(start / (GSM_SYMBOL_RATE * d_OSR)); - if ((current_time - d_last_time) > 0.1) { - pmt::pmt_t msg = pmt::make_tuple(pmt::mp("current_time"), - pmt::from_double(current_time)); - message_port_pub(pmt::mp("measurements"), msg); - d_last_time = current_time; - } - - /* Frequency correction loop */ + /* Frequency correction loop */ //TODO: move this to FCCH burst processing + d_freq_offset_tag_in_fcch = false; std::vector freq_offset_tags; pmt::pmt_t key = pmt::string_to_symbol("setting_freq_offset"); - get_tags_in_range(freq_offset_tags, 0, start, stop, key); + get_tags_in_window(freq_offset_tags, 0, 0, noutput_items, key); if (!freq_offset_tags.empty()) { tag_t freq_offset_tag = freq_offset_tags[0]; - uint64_t tag_offset = freq_offset_tag.offset - start; + uint64_t tag_offset = freq_offset_tag.offset - nitems_read(0); d_freq_offset_setting = pmt::to_double(freq_offset_tag.value); burst_type b_type = d_channel_conf.get_burst_type(d_burst_nr); @@ -189,10 +171,6 @@ namespace gr d_freq_offset_tag_in_fcch = tag_offset < last_sample_nr; } } - - /* Obtaining current time with use of rx_time tag provided i.e. by UHD devices */ - /* And storing it in time_sample_ref for sample number to time conversion */ - std::vector rx_time_tags; /* Main state machine */ d_samples_consumed = 0; @@ -208,17 +186,43 @@ namespace gr break; } - get_tags_in_window(rx_time_tags, 0, 0, d_samples_consumed, pmt::string_to_symbol("rx_time")); - if(!rx_time_tags.empty()){ - d_rx_time_received = true; - tag_t rx_time_tag = *(rx_time_tags.begin()); + /* Time synchronization */ + /* Obtaining current time with use of rx_time tag provided i.e. by UHD devices */ + /* and storing it in time_sample_ref for sample number to time conversion */ - uint64_t rx_time_full_part = to_uint64(tuple_ref(rx_time_tag.value,0)); - double rx_time_frac_part = to_double(tuple_ref(rx_time_tag.value,1)); - - time_spec_t current_rx_time = time_spec_t(rx_time_full_part, rx_time_frac_part); - uint64_t current_start_offset = rx_time_tag.offset; - d_time_samp_ref.update(current_rx_time, current_start_offset); + typedef std::vector::iterator tag_iter; + std::vector all_tags; + get_tags_in_window(all_tags, 0, 0, d_samples_consumed); + + for(tag_iter itag = all_tags.begin(); itag != all_tags.end(); itag++){ + if(pmt::eqv(itag->key, pmt::mp("set_resamp_ratio"))){ + double resamp_rate = pmt::to_double(itag->value); + uint64_t N = get_offset_before_resampler(itag->offset); + d_time_samp_ref.update(itag->offset, N, resamp_rate); + } + else if(pmt::eqv(itag->key, pmt::mp("rx_time"))){ + d_rx_time_received = true; + uint64_t N = get_offset_before_resampler(itag->offset); + time_spec_t rx_time = time_spec_t( + pmt::to_uint64(tuple_ref(itag->value,0)), + pmt::to_double(tuple_ref(itag->value,1)) + ); + d_time_samp_ref.update(itag->offset, N, rx_time); + } +// else if(pmt::eqv(itag->key, pmt::mp("rx_rate"))){ //to jest źle TODO extra: zamienić to na update samp_rate dal clock_offset_controllera, a on następnie ustawiałby resamp_rate w sterowanym resamplerze +// d_rx_time_received = true; +// d_time_samp_ref.set_samp_rate(pmt::to_double(itag->value)); +// } + } + + /* Send updates of time for clock offset controller every 0.1 second */ + time_spec_t current_time = d_time_samp_ref.convert_M_to_ideal_t(nitems_read(0)); + + if ((current_time - d_last_time).get_real_secs() > 0.1) { + pmt::pmt_t msg = pmt::make_tuple(pmt::mp("current_time"), + pmt::from_double(current_time.get_real_secs())); //TODO: przerobić to na parę pmt i w bloku controllera przerabiać to na time_spec_t + message_port_pub(pmt::mp("measurements"), msg); + d_last_time = current_time; } return d_samples_consumed; @@ -1059,12 +1063,15 @@ namespace gr pmt::pmt_t pdu_header = pmt::make_dict(); /* Add timestamp of the first sample - if available */ - if(d_rx_time_received) { - time_spec_t time_spec_of_first_sample = d_time_samp_ref.offset_to_time(nitems_read(0)+burst_start); + if(d_rx_time_received && burst_start != -1) { + time_spec_t time_spec_of_first_sample = + d_time_samp_ref.convert_M_to_t(nitems_read(0) + burst_start); uint64_t full = time_spec_of_first_sample.get_full_secs(); double frac = time_spec_of_first_sample.get_frac_secs(); - pdu_header = - pmt::dict_add(pdu_header, pmt::mp("fn_time"), + int64_t N = d_time_samp_ref.convert_M_to_N(nitems_read(0) + burst_start); + time_spec_t ref_t = d_time_samp_ref.get_ref_time(); + pdu_header = + pmt::dict_add(pdu_header, pmt::mp("fn_time"), pmt::cons( pmt::cons(pmt::from_uint64(be32toh(frame_number)), pmt::from_uint64(tn)), pmt::cons(pmt::from_uint64(full), pmt::from_double(frac)))); diff --git a/lib/receiver/receiver_impl.h b/lib/receiver/receiver_impl.h index 8714d52..bc11879 100644 --- a/lib/receiver/receiver_impl.h +++ b/lib/receiver/receiver_impl.h @@ -54,7 +54,7 @@ namespace gr { gr_complex d_sch_training_seq[N_SYNC_BITS]; /// original_ofsets; + get_tags_in_window(original_ofsets, 0, offset, offset+1, pmt::intern("original_offset")); + uint64_t offset_recovered = d_time_samp_ref.convert_M_to_N(offset); + uint64_t offset_before_resampler = 0; + if(!original_ofsets.empty()){ + offset_before_resampler = pmt::to_uint64(original_ofsets[0].value); + } else { + offset_before_resampler = offset_recovered; + } + return offset_before_resampler; + }; void synchronized_handler(gr_complex *input, gr_vector_const_void_star &input_items, int noutput_items); diff --git a/lib/receiver/time_sample_ref.cc b/lib/receiver/time_sample_ref.cc index f68abd0..f21bdd5 100644 --- a/lib/receiver/time_sample_ref.cc +++ b/lib/receiver/time_sample_ref.cc @@ -25,32 +25,17 @@ namespace gr { namespace gsm { - time_sample_ref::time_sample_ref(double samp_rate): d_samp_rate(samp_rate) + time_sample_ref::time_sample_ref(double samp_rate, double resamp_rate): + d_samp_rate(samp_rate), + d_resamp_rate(resamp_rate), + d_ref_time(0.0), + d_ref_offset(0), + d_original_ref_offset(0) { } - - time_sample_ref::~time_sample_ref() - { - } - - void time_sample_ref::update(time_spec_t last_rx_time, uint64_t current_start_offset) - { - d_last_rx_time = last_rx_time; - d_current_start_offset = current_start_offset; - } - - time_spec_t time_sample_ref::offset_to_time(uint64_t offset) - { - uint64_t samples_from_last_rx_time = offset - d_current_start_offset; - time_spec_t time = time_spec_t::from_ticks(samples_from_last_rx_time, d_samp_rate) + d_last_rx_time; - return time; - } - uint64_t time_sample_ref::time_to_offset(time_spec_t time) + time_sample_ref::~time_sample_ref() { - uint64_t samples_since_last_rx_time_tag = (time-d_last_rx_time).to_ticks(d_samp_rate); - uint64_t offset = samples_since_last_rx_time_tag + d_current_start_offset; - return offset; } } // namespace gsm } // namespace gr diff --git a/lib/receiver/time_sample_ref.h b/lib/receiver/time_sample_ref.h index 793944d..200ba48 100644 --- a/lib/receiver/time_sample_ref.h +++ b/lib/receiver/time_sample_ref.h @@ -29,20 +29,79 @@ namespace gr { namespace gsm { /* - Class for storing time reference and for conversions time<->sample number + Class for storing time reference and for conversions time<->sample number. + N - number of sample before the resampler + M - number of sample after the resampler + t - time before the resampler */ class time_sample_ref { private: - double d_samp_rate; - time_spec_t d_last_rx_time; - uint64_t d_current_start_offset; + double d_samp_rate; + double d_resamp_rate; + + time_spec_t d_ref_time; + uint64_t d_ref_offset;/**< sample offset of the reference point */ + uint64_t d_original_ref_offset;/**< sample offset of the + reference point before resampler */ public: - time_sample_ref(double samp_rate); - ~time_sample_ref(); - void update(time_spec_t last_rx_time, uint64_t current_start_offset); - time_spec_t offset_to_time(uint64_t offset); - uint64_t time_to_offset(time_spec_t time); + time_sample_ref(double samp_rate, double resamp_rate = 1.0); + ~time_sample_ref(); + + void update(uint64_t M, uint64_t N){ + d_ref_time = time_spec_t::from_ticks(N - d_original_ref_offset, d_samp_rate*d_resamp_rate) + d_ref_time; + d_original_ref_offset = N; + d_ref_offset = M; + } + + void update(uint64_t M, uint64_t N, double resamp_rate){ +// std::cout << "M:" << M << " N:" << N << " resamp_rate:" << resamp_rate << std::endl; + update(M, N); + d_resamp_rate = resamp_rate; + } + void update(uint64_t M, uint64_t N, time_spec_t ref_time){ +// std::cout << "M:" << M << " N:" << N << " ref_time" << ref_time.get_real_secs() << std::endl; + update(M, N); + d_ref_time = ref_time; + } + + void set_samp_rate(double samp_rate){ + d_samp_rate = samp_rate; + } + + uint64_t convert_M_to_N(uint64_t M){ + time_spec_t new_N_tspec = + time_spec_t::from_ticks(M - d_ref_offset, 1.0/d_resamp_rate) + + time_spec_t::from_ticks(d_original_ref_offset, 1.0); + uint64_t new_N = (round(new_N_tspec.get_real_secs())); +// std::cout << "d_ref_offset:" << d_ref_offset << " d_resamp_rate:" << d_resamp_rate << " d_ref_offset" << d_ref_offset << " d_original_ref_offset:" << d_original_ref_offset << std::endl; + return new_N; + } + + time_spec_t convert_M_to_t(uint64_t M){ + return time_spec_t::from_ticks(convert_M_to_N(M) - + d_original_ref_offset, d_samp_rate*d_resamp_rate) + d_ref_time; + } + + time_spec_t convert_M_to_ideal_t(uint64_t M){ + return time_spec_t::from_ticks(M, d_samp_rate); + } + + time_spec_t get_ref_time(){ + return d_ref_time; + } + + uint64_t convert_t_to_M(time_spec_t time) + { + uint64_t samples_since_ref_time_tag = (time-d_ref_time).to_ticks(d_samp_rate); + uint64_t offset = samples_since_ref_time_tag + d_ref_offset; + return offset; + } + + uint64_t convert_t_to_N(time_spec_t time) + { + return convert_M_to_N(convert_t_to_M(time)); + } }; } // namespace gsm } // namespace gr -- cgit v1.2.3