From 4089c1a7f3dbadf024b160667ebbd273d77984dd Mon Sep 17 00:00:00 2001 From: piotr Date: Wed, 6 Aug 2014 14:10:56 +0200 Subject: Added new blocks for clock freqeuncy correction --- grc/CMakeLists.txt | 6 +- grc/gsm_clock_offset_control.xml | 33 ++ grc/gsm_clock_offset_corrector.xml | 50 ++ grc/gsm_controlled_const_source_f.xml | 27 + grc/gsm_controlled_rotator_cc.xml | 38 ++ hier_blocks/clock_offset_corrector.grc | 652 +++++++++++++++++++++++ include/gsm/controlled_const_source_f.h | 57 ++ include/gsm/controlled_rotator_cc.h | 59 ++ include/gsm/message_printer.h | 56 ++ lib/CMakeLists.txt | 3 + lib/misc_utils/controlled_const_source_f_impl.cc | 86 +++ lib/misc_utils/controlled_const_source_f_impl.h | 51 ++ lib/misc_utils/controlled_rotator_cc_impl.cc | 107 ++++ lib/misc_utils/controlled_rotator_cc_impl.h | 53 ++ lib/receiver/receiver_impl.cc | 108 ++-- lib/receiver/receiver_impl.h | 19 +- python/CMakeLists.txt | 2 +- python/__init__.py | 1 + python/clock_offset_control.py | 76 +++ python/clock_offset_corrector.py | 82 +++ python/receiver_hier.py | 2 + 21 files changed, 1502 insertions(+), 66 deletions(-) create mode 100644 grc/gsm_clock_offset_control.xml create mode 100644 grc/gsm_clock_offset_corrector.xml create mode 100644 grc/gsm_controlled_const_source_f.xml create mode 100644 grc/gsm_controlled_rotator_cc.xml create mode 100644 hier_blocks/clock_offset_corrector.grc create mode 100644 include/gsm/controlled_const_source_f.h create mode 100644 include/gsm/controlled_rotator_cc.h create mode 100644 include/gsm/message_printer.h create mode 100644 lib/misc_utils/controlled_const_source_f_impl.cc create mode 100644 lib/misc_utils/controlled_const_source_f_impl.h create mode 100644 lib/misc_utils/controlled_rotator_cc_impl.cc create mode 100644 lib/misc_utils/controlled_rotator_cc_impl.h create mode 100644 python/clock_offset_control.py create mode 100644 python/clock_offset_corrector.py diff --git a/grc/CMakeLists.txt b/grc/CMakeLists.txt index dc25035..0f0db50 100644 --- a/grc/CMakeLists.txt +++ b/grc/CMakeLists.txt @@ -24,5 +24,9 @@ install(FILES gsm_fcch_detector.xml gsm_get_bcch_or_ccch_bursts.xml gsm_control_channels_decoder.xml - gsm_extract_system_info.xml DESTINATION share/gnuradio/grc/blocks + gsm_extract_system_info.xml + gsm_controlled_rotator_cc.xml + gsm_controlled_const_source_f.xml + gsm_clock_offset_control.xml + gsm_message_printer.xml DESTINATION share/gnuradio/grc/blocks ) diff --git a/grc/gsm_clock_offset_control.xml b/grc/gsm_clock_offset_control.xml new file mode 100644 index 0000000..bb4dadb --- /dev/null +++ b/grc/gsm_clock_offset_control.xml @@ -0,0 +1,33 @@ + + + Clock offset control + gsm_clock_offset_control + GSM + import gsm + gsm.clock_offset_control($fc, $samp_rate) + + + fc + fc + fc + float + + + + samp_rate + samp_rate + samp_rate + float + + + + measurements + message + + + + ppm + message + 1 + + diff --git a/grc/gsm_clock_offset_corrector.xml b/grc/gsm_clock_offset_corrector.xml new file mode 100644 index 0000000..e9aa7db --- /dev/null +++ b/grc/gsm_clock_offset_corrector.xml @@ -0,0 +1,50 @@ + + + Clock offset corrector + clock_offset_corrector + GSM + execfile("/home/piotr/.grc_gnuradio/clock_offset_corrector.py") + clock_offset_corrector( + ppm=$ppm, + samp_rate=$samp_rate, + fc=$fc, +) + set_ppm($ppm) + set_samp_rate($samp_rate) + set_fc($fc) + + ppm + ppm + 0 + raw + + + samp_rate + samp_rate + 1625000.0/6.0*4.0 + raw + + + fc + fc + 936.6e6 + raw + + + in + complex + 1 + + + ppm_msg + message + True + + + out + complex + 1 + + Piotr Krysik + gr-gsm/examples/clock_offset_corrector.grc + diff --git a/grc/gsm_controlled_const_source_f.xml b/grc/gsm_controlled_const_source_f.xml new file mode 100644 index 0000000..f51ef69 --- /dev/null +++ b/grc/gsm_controlled_const_source_f.xml @@ -0,0 +1,27 @@ + + + Controlled const source + gsm_controlled_const_source_f + GSM + import gsm + gsm.controlled_const_source_f($constant) + set_constant($constant) + + + constant + constant + 0 + float + + + + constant_msg + message + 1 + + + + out + float + + diff --git a/grc/gsm_controlled_rotator_cc.xml b/grc/gsm_controlled_rotator_cc.xml new file mode 100644 index 0000000..83040d4 --- /dev/null +++ b/grc/gsm_controlled_rotator_cc.xml @@ -0,0 +1,38 @@ + + + Controlled rotator + gsm_controlled_rotator_cc + GSM + import gsm + gsm.controlled_rotator_cc($phase_inc,$samp_rate) + set_phase_inc($phase_inc) + set_samp_rate($samp_rate) + + phase_inc + phase_inc + 0 + real + + + + samp_rate + samp_rate + samp_rate + real + + + + in + complex + + + phase_inc + float + + + + out + complex + + + diff --git a/hier_blocks/clock_offset_corrector.grc b/hier_blocks/clock_offset_corrector.grc new file mode 100644 index 0000000..56a70d7 --- /dev/null +++ b/hier_blocks/clock_offset_corrector.grc @@ -0,0 +1,652 @@ + + + Wed Aug 6 13:47:37 2014 + + options + + id + clock_offset_corrector + + + _enabled + True + + + title + Clock offset corrector + + + author + Piotr Krysik + + + description + + + + window_size + 1280, 1024 + + + generate_options + hb + + + category + GSM + + + run_options + prompt + + + run + True + + + max_nouts + 0 + + + realtime_scheduling + + + + alias + + + + _coordinate + (10, 10) + + + _rotation + 0 + + + + parameter + + id + ppm + + + _enabled + True + + + label + ppm + + + value + 0 + + + type + eng_float + + + short_id + + + + alias + + + + _coordinate + (487, 22) + + + _rotation + 0 + + + + parameter + + id + samp_rate + + + _enabled + True + + + label + samp_rate + + + value + 1625000.0/6.0*4.0 + + + type + eng_float + + + short_id + + + + alias + + + + _coordinate + (368, 19) + + + _rotation + 0 + + + + parameter + + id + fc + + + _enabled + True + + + label + fc + + + value + 936.6e6 + + + type + eng_float + + + short_id + + + + alias + + + + _coordinate + (274, 19) + + + _rotation + 0 + + + + import + + id + math_imp + + + _enabled + True + + + import + import math + + + alias + + + + _coordinate + (11, 125) + + + _rotation + 0 + + + + blocks_multiply_const_vxx + + id + blocks_multiply_const_vxx_0 + + + _enabled + True + + + type + float + + + const + fc/samp_rate*(2*math.pi) + + + vlen + 1 + + + alias + + + + affinity + + + + minoutbuf + 0 + + + maxoutbuf + 0 + + + _coordinate + (571, 335) + + + _rotation + 0 + + + + blocks_add_const_vxx + + id + blocks_add_const_vxx_0 + + + _enabled + True + + + type + float + + + const + 1 + + + vlen + 1 + + + alias + + + + affinity + + + + minoutbuf + 0 + + + maxoutbuf + 0 + + + _coordinate + (779, 217) + + + _rotation + 0 + + + + blocks_multiply_const_vxx + + id + blocks_multiply_const_vxx_0_0 + + + _enabled + True + + + type + float + + + const + 1.0/1.0e6 + + + vlen + 1 + + + alias + + + + affinity + + + + minoutbuf + 0 + + + maxoutbuf + 0 + + + _coordinate + (405, 217) + + + _rotation + 0 + + + + pad_source + + id + pad_source_0 + + + _enabled + True + + + label + in + + + type + complex + + + vlen + 1 + + + num_streams + 1 + + + optional + False + + + _coordinate + (13, 301) + + + _rotation + 0 + + + + pad_sink + + id + pad_sink_1 + + + _enabled + True + + + label + out + + + type + complex + + + vlen + 1 + + + num_streams + 1 + + + optional + False + + + _coordinate + (1174, 335) + + + _rotation + 0 + + + + fractional_resampler_xx + + id + fractional_resampler_xx_0 + + + _enabled + True + + + type + complex + + + phase_shift + 0 + + + resamp_ratio + 1 + + + alias + + + + affinity + + + + minoutbuf + 0 + + + maxoutbuf + 0 + + + _coordinate + (986, 322) + + + _rotation + 0 + + + + pad_source + + id + ppm_msg + + + _enabled + True + + + label + ppm_msg + + + type + message + + + vlen + 1 + + + num_streams + 1 + + + optional + True + + + _coordinate + (15, 217) + + + _rotation + 0 + + + + gsm_controlled_const_source_f + + id + gsm_controlled_const_source_f_0 + + + _enabled + True + + + constant + ppm + + + alias + + + + affinity + + + + minoutbuf + 0 + + + maxoutbuf + 0 + + + _coordinate + (183, 217) + + + _rotation + 0 + + + + gsm_controlled_rotator_cc + + id + gsm_controlled_rotator_cc_0 + + + _enabled + True + + + phase_inc + 0 + + + samp_rate + samp_rate + + + alias + + + + affinity + + + + minoutbuf + 0 + + + maxoutbuf + 0 + + + _coordinate + (759, 305) + + + _rotation + 0 + + + + gsm_controlled_const_source_f_0 + blocks_multiply_const_vxx_0_0 + 0 + 0 + + + pad_source_0 + gsm_controlled_rotator_cc_0 + 0 + 0 + + + blocks_multiply_const_vxx_0_0 + blocks_multiply_const_vxx_0 + 0 + 0 + + + blocks_add_const_vxx_0 + fractional_resampler_xx_0 + 0 + 1 + + + gsm_controlled_rotator_cc_0 + fractional_resampler_xx_0 + 0 + 0 + + + blocks_multiply_const_vxx_0 + gsm_controlled_rotator_cc_0 + 0 + 1 + + + blocks_multiply_const_vxx_0_0 + blocks_add_const_vxx_0 + 0 + 0 + + + fractional_resampler_xx_0 + pad_sink_1 + 0 + 0 + + + ppm_msg + gsm_controlled_const_source_f_0 + 0 + constant_msg + + diff --git a/include/gsm/controlled_const_source_f.h b/include/gsm/controlled_const_source_f.h new file mode 100644 index 0000000..b175578 --- /dev/null +++ b/include/gsm/controlled_const_source_f.h @@ -0,0 +1,57 @@ +/* -*- c++ -*- */ +/* + * Copyright 2014 <+YOU OR YOUR COMPANY+>. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + + +#ifndef INCLUDED_GSM_CONTROLLED_CONST_SOURCE_F_H +#define INCLUDED_GSM_CONTROLLED_CONST_SOURCE_F_H + +#include +#include + +namespace gr { + namespace gsm { + + /*! + * \brief <+description of block+> + * \ingroup gsm + * + */ + class GSM_API controlled_const_source_f : virtual public gr::sync_block + { + public: + typedef boost::shared_ptr sptr; + + /*! + * \brief Return a shared_ptr to a new instance of gsm::controlled_const_source_f. + * + * To avoid accidental use of raw pointers, gsm::controlled_const_source_f's + * constructor is in a private implementation + * class. gsm::controlled_const_source_f::make is the public interface for + * creating new instances. + */ + static sptr make(float constant); + virtual void set_constant(float constant) = 0; + }; + + } // namespace gsm +} // namespace gr + +#endif /* INCLUDED_GSM_CONTROLLED_CONST_SOURCE_F_H */ + diff --git a/include/gsm/controlled_rotator_cc.h b/include/gsm/controlled_rotator_cc.h new file mode 100644 index 0000000..5339660 --- /dev/null +++ b/include/gsm/controlled_rotator_cc.h @@ -0,0 +1,59 @@ +/* -*- c++ -*- */ +/* + * Copyright 2014 <+YOU OR YOUR COMPANY+>. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + + +#ifndef INCLUDED_GSM_CONTROLLED_ROTATOR_CC_H +#define INCLUDED_GSM_CONTROLLED_ROTATOR_CC_H + +#include +#include + +namespace gr { + namespace gsm { + + /*! + * \brief <+description of block+> + * \ingroup gsm + * + */ + class GSM_API controlled_rotator_cc : virtual public sync_block + { + public: + typedef boost::shared_ptr sptr; + + /*! + * \brief Return a shared_ptr to a new instance of gsm::controlled_rotator_cc. + * + * To avoid accidental use of raw pointers, gsm::controlled_rotator_cc's + * constructor is in a private implementation + * class. gsm::controlled_rotator_cc::make is the public interface for + * creating new instances. + */ + static sptr make(double phase_inc, double samp_rate); + + virtual void set_phase_inc(double phase_inc) = 0; + virtual void set_samp_rate(double samp_rate) = 0; + }; + + } // namespace gsm +} // namespace gr + +#endif /* INCLUDED_GSM_CONTROLLED_ROTATOR_CC_H */ + diff --git a/include/gsm/message_printer.h b/include/gsm/message_printer.h new file mode 100644 index 0000000..e049934 --- /dev/null +++ b/include/gsm/message_printer.h @@ -0,0 +1,56 @@ +/* -*- c++ -*- */ +/* + * Copyright 2014 <+YOU OR YOUR COMPANY+>. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + + +#ifndef INCLUDED_GSM_MESSAGE_PRINTER_H +#define INCLUDED_GSM_MESSAGE_PRINTER_H + +#include +#include + +namespace gr { + namespace gsm { + + /*! + * \brief <+description of block+> + * \ingroup gsm + * + */ + class GSM_API message_printer : virtual public gr::block + { + public: + typedef boost::shared_ptr sptr; + + /*! + * \brief Return a shared_ptr to a new instance of gsm::message_printer. + * + * To avoid accidental use of raw pointers, gsm::message_printer's + * constructor is in a private implementation + * class. gsm::message_printer::make is the public interface for + * creating new instances. + */ + static sptr make(); + }; + + } // namespace gsm +} // namespace gr + +#endif /* INCLUDED_GSM_MESSAGE_PRINTER_H */ + diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 5a730b9..e4ed842 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -35,6 +35,9 @@ list(APPEND gsm_sources decoding/control_channels_decoder_impl.cc decoding/cch.c decoding/fire_crc.c + misc_utils/controlled_rotator_cc_impl.cc + misc_utils/controlled_const_source_f_impl.cc + misc_utils/message_printer_impl.cc ) add_library(gnuradio-gsm SHARED ${gsm_sources}) diff --git a/lib/misc_utils/controlled_const_source_f_impl.cc b/lib/misc_utils/controlled_const_source_f_impl.cc new file mode 100644 index 0000000..d58f04f --- /dev/null +++ b/lib/misc_utils/controlled_const_source_f_impl.cc @@ -0,0 +1,86 @@ +/* -*- c++ -*- */ +/* + * Copyright 2014 <+YOU OR YOUR COMPANY+>. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include "controlled_const_source_f_impl.h" + +namespace gr { + namespace gsm { + + controlled_const_source_f::sptr + controlled_const_source_f::make(float constant) + { + return gnuradio::get_initial_sptr + (new controlled_const_source_f_impl(constant)); + } + + void controlled_const_source_f_impl::set_constant_msg(pmt::pmt_t msg){ + if(pmt::is_real(msg)){ + set_constant(pmt::to_double(msg)); + } + } + + /* + * The private constructor + */ + controlled_const_source_f_impl::controlled_const_source_f_impl(float constant) + : gr::sync_block("controlled_const_source_f", + gr::io_signature::make(0, 0, 0), + gr::io_signature::make(1, 1, sizeof(float))) + { + set_constant(constant); + message_port_register_in(pmt::mp("constant_msg")); + set_msg_handler(pmt::mp("constant_msg"), boost::bind(&controlled_const_source_f_impl::set_constant_msg, this, _1)); + } + + /* + * Our virtual destructor. + */ + controlled_const_source_f_impl::~controlled_const_source_f_impl() + { + } + + int + controlled_const_source_f_impl::work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) + { + float *optr = (float*)output_items[0]; + float t; + + t = d_constant; + std::fill_n(optr, noutput_items, t); + + return noutput_items; + } + + void controlled_const_source_f_impl::set_constant(float constant){ + d_constant = constant; + } + + + + } /* namespace gsm */ +} /* namespace gr */ + diff --git a/lib/misc_utils/controlled_const_source_f_impl.h b/lib/misc_utils/controlled_const_source_f_impl.h new file mode 100644 index 0000000..2abe812 --- /dev/null +++ b/lib/misc_utils/controlled_const_source_f_impl.h @@ -0,0 +1,51 @@ +/* -*- c++ -*- */ +/* + * Copyright 2014 <+YOU OR YOUR COMPANY+>. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_GSM_CONTROLLED_CONST_SOURCE_F_IMPL_H +#define INCLUDED_GSM_CONTROLLED_CONST_SOURCE_F_IMPL_H + +#include + +namespace gr { + namespace gsm { + + class controlled_const_source_f_impl : public controlled_const_source_f + { + private: + float d_constant; + void set_constant_msg(pmt::pmt_t msg); + + public: + controlled_const_source_f_impl(float constant); + ~controlled_const_source_f_impl(); + + // Where all the action really happens + int work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); + + virtual void set_constant(float constant); + }; + + } // namespace gsm +} // namespace gr + +#endif /* INCLUDED_GSM_CONTROLLED_CONST_SOURCE_F_IMPL_H */ + diff --git a/lib/misc_utils/controlled_rotator_cc_impl.cc b/lib/misc_utils/controlled_rotator_cc_impl.cc new file mode 100644 index 0000000..39fafb0 --- /dev/null +++ b/lib/misc_utils/controlled_rotator_cc_impl.cc @@ -0,0 +1,107 @@ +/* -*- c++ -*- */ +/* + * Copyright 2014 <+YOU OR YOUR COMPANY+>. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include "controlled_rotator_cc_impl.h" +#include +#include + +namespace gr { + namespace gsm { + + controlled_rotator_cc::sptr + controlled_rotator_cc::make(double phase_inc, double samp_rate) + { + return gnuradio::get_initial_sptr + (new controlled_rotator_cc_impl(phase_inc, samp_rate)); + } + + /* + * The private constructor + */ + controlled_rotator_cc_impl::controlled_rotator_cc_impl(double phase_inc, double samp_rate) + : gr::sync_block("controlled_rotator_cc", + gr::io_signature::make2(1, 2, sizeof(gr_complex), sizeof(float)), + gr::io_signature::make(1, 1, sizeof(gr_complex))) + { + set_phase_inc(phase_inc); + set_samp_rate(samp_rate); + } + + /* + * Our virtual destructor. + */ + controlled_rotator_cc_impl::~controlled_rotator_cc_impl() + { + } + + void + controlled_rotator_cc_impl::set_phase_inc(double phase_inc) + { + d_phase_inc = phase_inc; + d_r.set_phase_incr( exp(gr_complex(0, (double)phase_inc)) ); + } + + void + controlled_rotator_cc_impl::set_samp_rate(double samp_rate) + { + d_samp_rate = samp_rate; + } + + int + controlled_rotator_cc_impl::work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) + { + const gr_complex *in = (const gr_complex *)input_items[0]; + gr_complex *out = (gr_complex *)output_items[0]; + + if(input_items.size() == 2) { + int ii=0; + const float *pp = (const float *)input_items[1]; + + while(ii < noutput_items){ + //look for different values on phase increment control input + if(d_phase_inc != (*pp)){ + set_phase_inc(*(pp)); //set new value of phase increment + + float freq_offset_setting = (*(pp) / (2*M_PI)) * d_samp_rate; //send stream tag with a new value of the frequency offset + int offset = nitems_written(0); + pmt::pmt_t key = pmt::string_to_symbol("setting_freq_offset"); + pmt::pmt_t value = pmt::from_double(freq_offset_setting); + add_item_tag(0,offset, key, value); + + break; + } + pp++; + ii++; + } + } + d_r.rotateN(out, in, noutput_items); + return noutput_items; + } + + } /* namespace gsm */ +} /* namespace gr */ + diff --git a/lib/misc_utils/controlled_rotator_cc_impl.h b/lib/misc_utils/controlled_rotator_cc_impl.h new file mode 100644 index 0000000..2dbad6c --- /dev/null +++ b/lib/misc_utils/controlled_rotator_cc_impl.h @@ -0,0 +1,53 @@ +/* -*- c++ -*- */ +/* + * Copyright 2014 <+YOU OR YOUR COMPANY+>. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_GSM_CONTROLLED_ROTATOR_CC_IMPL_H +#define INCLUDED_GSM_CONTROLLED_ROTATOR_CC_IMPL_H + +#include +#include + +namespace gr { + namespace gsm { + + class controlled_rotator_cc_impl : public controlled_rotator_cc + { + private: + gr_complex d_phase_inc; + double d_samp_rate; + blocks::rotator d_r; + + public: + controlled_rotator_cc_impl(double phase_inc, double samp_rate); + ~controlled_rotator_cc_impl(); + + virtual void set_phase_inc(double phase_inc); + virtual void set_samp_rate(double samp_rate); + // Where all the action really happens + int work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); + }; + + } // namespace gsm +} // namespace gr + +#endif /* INCLUDED_GSM_CONTROLLED_ROTATOR_CC_IMPL_H */ + diff --git a/lib/receiver/receiver_impl.cc b/lib/receiver/receiver_impl.cc index 500113e..c333ccf 100644 --- a/lib/receiver/receiver_impl.cc +++ b/lib/receiver/receiver_impl.cc @@ -71,7 +71,7 @@ receiver_impl::receiver_impl(feval_dd * tuner, int osr, int arfcn) d_tuner(tuner), d_counter(0), d_fcch_start_pos(0), - d_freq_offset(0), + d_freq_offset_setting(0), d_state(first_fcch_search), d_burst_nr(osr), d_failed_sch(0), @@ -79,7 +79,7 @@ receiver_impl::receiver_impl(feval_dd * tuner, int osr, int arfcn) d_signal_dbm(-120) { int i; - //set_output_multiple(floor((TS_BITS + 2 * GUARD_PERIOD) * d_OSR)); //don't send samples to the receiver until there are at least samples for one + //don't send samples to the receiver until there are at least samples for one set_output_multiple(floor((TS_BITS + 2 * GUARD_PERIOD) * d_OSR)); // burst and two gurad periods (one gurard period is an arbitrary overlap) gmsk_mapper(SYNC_BITS, N_SYNC_BITS, d_sch_training_seq, gr_complex(0.0, -1.0)); for (i = 0; i < TRAIN_SEQ_NUM; i++) @@ -89,11 +89,10 @@ receiver_impl::receiver_impl(feval_dd * tuner, int osr, int arfcn) gmsk_mapper(train_seq[i], N_TRAIN_BITS, d_norm_training_seq[i], startpoint); } message_port_register_out(pmt::mp("bursts")); + message_port_register_out(pmt::mp("measurements")); configure_receiver(); //configure the receiver - tell it where to find which burst type } - - /* * Our virtual destructor. */ @@ -106,19 +105,44 @@ receiver_impl::work(int noutput_items, gr_vector_const_void_star &input_items, gr_vector_void_star &output_items) { - //std::cout << noutput_items << std::endl; const gr_complex *input = (const gr_complex *) input_items[0]; - + std::vector freq_offset_tags; + uint64_t start = nitems_read(0); + uint64_t stop = start + noutput_items; + + pmt::pmt_t key = pmt::string_to_symbol("setting_freq_offset"); + get_tags_in_range(freq_offset_tags, 0, start, stop, key); + bool freq_offset_tag_in_fcch = false; + uint64_t tag_offset=-1; //-1 - just some clearly invalid value + + if(!freq_offset_tags.empty()){ + tag_t freq_offset_tag = freq_offset_tags[0]; + tag_offset = freq_offset_tag.offset - start; + + burst_type b_type = d_channel_conf.get_burst_type(d_burst_nr); + if(d_state == synchronized && b_type == fcch_burst){ + uint64_t last_sample_nr = ceil((GUARD_PERIOD + 2.0 * TAIL_BITS + 156.25) * d_OSR) + 1; + if(tag_offset < last_sample_nr){ + DCOUT("Freq change inside FCCH burst!!!!!!!!!!!!!!"); + freq_offset_tag_in_fcch = true; + } + d_freq_offset_setting = pmt::to_double(freq_offset_tag.value); + } else { + d_freq_offset_setting = pmt::to_double(freq_offset_tag.value); + } + } + switch (d_state) { //bootstrapping case first_fcch_search: DCOUT("FCCH search"); - if (find_fcch_burst(input, noutput_items)) //find frequency correction burst in the input buffer + double freq_offset_tmp; + if (find_fcch_burst(input, noutput_items, freq_offset_tmp)) //find frequency correction burst in the input buffer { - //set_frequency(d_freq_offset); //if fcch search is successful set frequency offset - DCOUT("Freq offset " << d_freq_offset); - DCOUT("PPM: " << d_freq_offset/940e6); + pmt::pmt_t msg = pmt::make_tuple(pmt::mp("freq_offset"),pmt::from_double(freq_offset_tmp-d_freq_offset_setting),pmt::mp("first_fcch_search")); + message_port_pub(pmt::mp("measurements"), msg); + d_state = next_fcch_search; } else @@ -130,15 +154,12 @@ receiver_impl::work(int noutput_items, case next_fcch_search: //this state is used because it takes some time (a bunch of buffered samples) { DCOUT("NEXT FCCH search"); - d_prev_freq_offset = d_freq_offset; //before previous set_frequqency cause change - if (find_fcch_burst(input, noutput_items)) + double freq_offset_tmp; + if (find_fcch_burst(input, noutput_items,freq_offset_tmp)) { - if (abs(d_prev_freq_offset - d_freq_offset) > FCCH_MAX_FREQ_OFFSET) - { - //set_frequency(d_freq_offset); //call set_frequncy only frequency offset change is greater than some value - //COUT("Freq offset " << d_freq_offset); - DCOUT("PPM: " << d_freq_offset/940); - } + pmt::pmt_t msg = pmt::make_tuple(pmt::mp("freq_offset"),pmt::from_double(freq_offset_tmp-d_freq_offset_setting),pmt::mp("next_fcch_search")); + message_port_pub(pmt::mp("measurements"), msg); + d_state = sch_search; } else @@ -148,7 +169,6 @@ receiver_impl::work(int noutput_items, break; } - case sch_search: { DCOUT("SCH search"); @@ -206,30 +226,24 @@ receiver_impl::work(int noutput_items, { const unsigned first_sample = ceil((GUARD_PERIOD + 2 * TAIL_BITS) * d_OSR) + 1; const unsigned last_sample = first_sample + USEFUL_BITS * d_OSR - TAIL_BITS * d_OSR; - double freq_offset = compute_freq_offset(input, first_sample, last_sample); //extract frequency offset from it + double freq_offset_tmp = compute_freq_offset(input, first_sample, last_sample); //extract frequency offset from it - d_freq_offset_vals.push_front(freq_offset); send_burst(d_burst_nr, fc_fb, b_type); - if (d_freq_offset_vals.size() >= 10) - { - double sum = std::accumulate(d_freq_offset_vals.begin(), d_freq_offset_vals.end(), 0); - double mean_offset = sum / d_freq_offset_vals.size(); //compute mean - d_freq_offset_vals.clear(); - DCOUT("mean offset" << mean_offset/940); - if (abs(mean_offset) > FCCH_MAX_FREQ_OFFSET) - { - //d_freq_offset -= mean_offset; //and adjust frequency if it have changed beyond - //set_frequency(d_freq_offset); //some limit - DCOUT("Adjusting frequency, new frequency offset: " << d_freq_offset << "\n"); - } - } + pmt::pmt_t msg = pmt::make_tuple(pmt::mp("freq_offset"),pmt::from_double(freq_offset_tmp-d_freq_offset_setting),pmt::mp("synchronized")); + message_port_pub(pmt::mp("measurements"), msg); } break; case sch_burst: //if it's SCH burst { int t1, t2, t3, d_ncc, d_bcc; burst_start = get_sch_chan_imp_resp(input, &channel_imp_resp[0]); //get channel impulse response + + if(d_prev_burst_start != burst_start){ + d_prev_burst_start = burst_start; + DCOUT("burst start" << burst_start); + } + detect_burst(input, &channel_imp_resp[0], burst_start, output_binary); //MLSE detection of bits send_burst(d_burst_nr, output_binary, b_type); if (decode_sch(&output_binary[3], &t1, &t2, &t3, &d_ncc, &d_bcc) == 0) //and decode SCH data @@ -247,10 +261,9 @@ receiver_impl::work(int noutput_items, if (d_failed_sch >= MAX_SCH_ERRORS) { d_state = next_fcch_search; - d_freq_offset_vals.clear(); - d_freq_offset=0; - //set_frequency(0); - COUT("Re-Synchronization!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); + pmt::pmt_t msg = pmt::make_tuple(pmt::mp("freq_offset"),pmt::from_double(0.0),pmt::mp("sync_loss")); + message_port_pub(pmt::mp("measurements"), msg); + DCOUT("Re-Synchronization!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); } } } @@ -268,9 +281,7 @@ receiver_impl::work(int noutput_items, { unsigned int normal_burst_start; float dummy_corr_max, normal_corr_max; - DCOUT("Dummy"); get_norm_chan_imp_resp(input, &channel_imp_resp[0], &dummy_corr_max, TS_DUMMY); - DCOUT("Normal"); normal_burst_start = get_norm_chan_imp_resp(input, &channel_imp_resp[0], &normal_corr_max, d_bcc); DCOUT("normal_corr_max: " << normal_corr_max << " dummy_corr_max:" << dummy_corr_max); @@ -305,8 +316,7 @@ receiver_impl::work(int noutput_items, return 0; } - -bool receiver_impl::find_fcch_burst(const gr_complex *input, const int nitems) +bool receiver_impl::find_fcch_burst(const gr_complex *input, const int nitems, double & computed_freq_offset) { circular_buffer_float phase_diff_buffer(FCCH_HITS_NEEDED * d_OSR); //circular buffer used to scan throug signal to find //best match for FCCH burst @@ -451,10 +461,8 @@ bool receiver_impl::find_fcch_burst(const gr_complex *input, const int nitems) //compute frequency offset double phase_offset = best_sum / FCCH_HITS_NEEDED; - double freq_offset = phase_offset * 1625000.0 / (12.0 * M_PI); - //d_freq_offset -= freq_offset; - d_freq_offset = freq_offset; - DCOUT("freq_offset: " << d_freq_offset); + double freq_offset = phase_offset * 1625000.0/6 / (2 * M_PI); //1625000.0/6 - GMSK symbol rate in GSM + computed_freq_offset = freq_offset; end = true; result = true; @@ -623,7 +631,6 @@ void receiver_impl::detect_burst(const gr_complex * input, gr_complex * chan_imp } } -//TODO consider placing this funtion in a separate class for signal processing void receiver_impl::gmsk_mapper(const unsigned char * input, int nitems, gr_complex * gmsk_output, gr_complex start_point) { gr_complex j = gr_complex(0.0, 1.0); @@ -645,7 +652,6 @@ void receiver_impl::gmsk_mapper(const unsigned char * input, int nitems, gr_comp } } -//TODO consider use of some generalized function for correlation and placing it in a separate class for signal processing gr_complex receiver_impl::correlate_sequence(const gr_complex * sequence, int length, const gr_complex * input) { gr_complex result(0.0, 0.0); @@ -662,7 +668,6 @@ gr_complex receiver_impl::correlate_sequence(const gr_complex * sequence, int le } //computes autocorrelation for positive arguments -//TODO consider placing this funtion in a separate class for signal processing inline void receiver_impl::autocorrelation(const gr_complex * input, gr_complex * out, int nitems) { int i, k; @@ -676,7 +681,6 @@ inline void receiver_impl::autocorrelation(const gr_complex * input, gr_complex } } -//TODO consider use of some generalized function for filtering and placing it in a separate class for signal processing inline void receiver_impl::mafi(const gr_complex * input, int nitems, gr_complex * filter, int filter_length, gr_complex * output) { int ii = 0, n, a; @@ -698,7 +702,6 @@ inline void receiver_impl::mafi(const gr_complex * input, int nitems, gr_complex } } -//TODO: get_norm_chan_imp_resp is similar to get_sch_chan_imp_resp - consider joining this two functions //especially computations of strongest_window_nr int receiver_impl::get_norm_chan_imp_resp(const gr_complex *input, gr_complex * chan_imp_resp, float *corr_max, int bcc) { @@ -774,8 +777,6 @@ int receiver_impl::get_norm_chan_imp_resp(const gr_complex *input, gr_complex * DCOUT("strongest_window_nr_new: " << strongest_window_nr); burst_start = search_start_pos + strongest_window_nr - TRAIN_POS * d_OSR; //compute first sample posiiton which corresponds to the first sample of the impulse response - //TRAIN_POS=3+57+1+6 - //TODO: describe this part in detail in documentation as this is crucial part for synchronization DCOUT("burst_start: " << burst_start); return burst_start; @@ -845,7 +846,6 @@ void receiver_impl::configure_receiver() void receiver_impl::set_arfcn(int arfcn) //!! { d_arfcn = arfcn; -// std::cout << "set arfcn:"< d_freq_offset_vals; @@ -89,19 +90,19 @@ namespace gr { /** Function whis is used to search a FCCH burst and to compute frequency offset before * "synchronized" state of the receiver * - * TODO: Describe the FCCH search algorithm in the documentation * @param input vector with input signal * @param nitems number of samples in the input vector * @return */ - bool find_fcch_burst(const gr_complex *input, const int nitems); + bool find_fcch_burst(const gr_complex *input, const int nitems, double & computed_freq_offset); /** Computes frequency offset from FCCH burst samples * - * @param input vector with input samples - * @param first_sample number of the first sample of the FCCH busrt - * @param last_sample number of the last sample of the FCCH busrt - * @return frequency offset + * @param[in] input vector with input samples + * @param[in] first_sample number of the first sample of the FCCH busrt + * @param[in] last_sample number of the last sample of the FCCH busrt + * @param[out] computed_freq_offset contains frequency offset estimate if FCCH burst was located + * @return true if frequency offset was faound */ double compute_freq_offset(const gr_complex * input, unsigned first_sample, unsigned last_sample); @@ -206,8 +207,6 @@ namespace gr { receiver_impl(feval_dd * tuner, int osr, int arfcn); ~receiver_impl(); -// void forecast(int noutput_items, gr_vector_int &ninput_items_required); - int work(int noutput_items, gr_vector_const_void_star &input_items, gr_vector_void_star &output_items); virtual void set_arfcn(int arfcn); virtual void reset(); diff --git a/python/CMakeLists.txt b/python/CMakeLists.txt index 651baa5..1b24d40 100644 --- a/python/CMakeLists.txt +++ b/python/CMakeLists.txt @@ -36,7 +36,7 @@ GR_PYTHON_INSTALL( sch_detector.py fcch_detector.py chirpz.py - DESTINATION ${GR_PYTHON_DIR}/gsm + clock_offset_control.py DESTINATION ${GR_PYTHON_DIR}/gsm ) ######################################################################## diff --git a/python/__init__.py b/python/__init__.py index cf25d1c..3428109 100644 --- a/python/__init__.py +++ b/python/__init__.py @@ -50,6 +50,7 @@ from receiver_hier import receiver_hier from fcch_burst_tagger import fcch_burst_tagger from sch_detector import sch_detector from fcch_detector import fcch_detector +from clock_offset_control import clock_offset_control # diff --git a/python/clock_offset_control.py b/python/clock_offset_control.py new file mode 100644 index 0000000..133368c --- /dev/null +++ b/python/clock_offset_control.py @@ -0,0 +1,76 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Copyright 2014 <+YOU OR YOUR COMPANY+>. +# +# This is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# This software is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this software; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +from numpy import * +from gnuradio import gr +import pmt + +class clock_offset_control(gr.basic_block): + """ + docstring for block clock_offset_control + """ + def __init__(self, fc, samp_rate): + gr.basic_block.__init__(self, + name="clock_offset_control", + in_sig=[], + out_sig=[]) + self.fc = fc + self.samp_rate = samp_rate + self.message_port_register_in(pmt.intern("measurements")) + self.set_msg_handler(pmt.intern("measurements"), self.process_measurement) + self.message_port_register_out(pmt.intern("ppm")) + self.alfa = 0.6 + self.ppm_estimate = -1e6 + self.first_measurement = True + self.counter = 0 + + def process_measurement(self,msg): + if pmt.is_tuple(msg): + key = pmt.symbol_to_string(pmt.tuple_ref(msg,0)) + if key == "freq_offset": + freq_offset = pmt.to_double(pmt.tuple_ref(msg,1)) + ppm = -freq_offset/self.fc*1.0e6 + state = pmt.symbol_to_string(pmt.tuple_ref(msg,2)) + + if state == "first_fcch_search" or state == "next_fcch_search": + msg_ppm = pmt.from_double(ppm) + self.message_port_pub(pmt.intern("ppm"), msg_ppm) + + if state == "synchronized": + if self.first_measurement: + self.ppm_estimate = ppm + self.first_measurement = False + else: + self.ppm_estimate = (1-self.alfa)*self.ppm_estimate+self.alfa*ppm + + if self.counter == 5: + self.counter = 0 + msg_ppm = pmt.from_double(ppm) + self.message_port_pub(pmt.intern("ppm"), msg_ppm) + else: + self.counter=self.counter+1 + + if state == "sync_loss": + self.ppm_estimate = -1e6 + self.counter = 0 + self.first_measurement = True + msg_ppm = pmt.from_double(0.0) + self.message_port_pub(pmt.intern("ppm"), msg_ppm) diff --git a/python/clock_offset_corrector.py b/python/clock_offset_corrector.py new file mode 100644 index 0000000..df97cca --- /dev/null +++ b/python/clock_offset_corrector.py @@ -0,0 +1,82 @@ +#!/usr/bin/env python +################################################## +# Gnuradio Python Flow Graph +# Title: Clock offset corrector +# Author: Piotr Krysik +# Generated: Wed Aug 6 13:47:38 2014 +################################################## + +from gnuradio import blocks +from gnuradio import filter +from gnuradio import gr +from gnuradio.filter import firdes +import gsm +import math + +class clock_offset_corrector(gr.hier_block2): + + def __init__(self, ppm=0, samp_rate=1625000.0/6.0*4.0, fc=936.6e6): + gr.hier_block2.__init__( + self, "Clock offset corrector", + gr.io_signature(1, 1, gr.sizeof_gr_complex*1), + gr.io_signature(1, 1, gr.sizeof_gr_complex*1), + ) + + ################################################## + # Parameters + ################################################## + self.ppm = ppm + self.samp_rate = samp_rate + self.fc = fc + + ################################################## + # Blocks + ################################################## + self.ppm_msg = None;self.message_port_register_hier_out("ppm_msg") + self.gsm_controlled_rotator_cc_0 = gsm.controlled_rotator_cc(0,samp_rate) + self.gsm_controlled_const_source_f_0 = gsm.controlled_const_source_f(ppm) + self.fractional_resampler_xx_0 = filter.fractional_resampler_cc(0, 1) + self.blocks_multiply_const_vxx_0_0 = blocks.multiply_const_vff((1.0/1.0e6, )) + self.blocks_multiply_const_vxx_0 = blocks.multiply_const_vff((fc/samp_rate*(2*math.pi), )) + self.blocks_add_const_vxx_0 = blocks.add_const_vff((1, )) + + ################################################## + # Connections + ################################################## + self.connect((self.gsm_controlled_const_source_f_0, 0), (self.blocks_multiply_const_vxx_0_0, 0)) + self.connect((self, 0), (self.gsm_controlled_rotator_cc_0, 0)) + self.connect((self.blocks_multiply_const_vxx_0_0, 0), (self.blocks_multiply_const_vxx_0, 0)) + self.connect((self.blocks_add_const_vxx_0, 0), (self.fractional_resampler_xx_0, 1)) + self.connect((self.gsm_controlled_rotator_cc_0, 0), (self.fractional_resampler_xx_0, 0)) + self.connect((self.blocks_multiply_const_vxx_0, 0), (self.gsm_controlled_rotator_cc_0, 1)) + self.connect((self.blocks_multiply_const_vxx_0_0, 0), (self.blocks_add_const_vxx_0, 0)) + self.connect((self.fractional_resampler_xx_0, 0), (self, 0)) + + ################################################## + # Asynch Message Connections + ################################################## + self.msg_connect(self, "ppm_msg", self.gsm_controlled_const_source_f_0, "constant_msg") + + + def get_ppm(self): + return self.ppm + + def set_ppm(self, ppm): + self.ppm = ppm + self.gsm_controlled_const_source_f_0.set_constant(self.ppm) + + def get_samp_rate(self): + return self.samp_rate + + def set_samp_rate(self, samp_rate): + self.samp_rate = samp_rate + self.blocks_multiply_const_vxx_0.set_k((self.fc/self.samp_rate*(2*math.pi), )) + self.gsm_controlled_rotator_cc_0.set_samp_rate(self.samp_rate) + + def get_fc(self): + return self.fc + + def set_fc(self, fc): + self.fc = fc + self.blocks_multiply_const_vxx_0.set_k((self.fc/self.samp_rate*(2*math.pi), )) + diff --git a/python/receiver_hier.py b/python/receiver_hier.py index 6a0b77f..fe9c636 100644 --- a/python/receiver_hier.py +++ b/python/receiver_hier.py @@ -25,6 +25,7 @@ class receiver_hier(gr.hier_block2): gsm_symb_rate = 1625000/6.0 self.message_port_register_hier_in("bursts") + self.message_port_register_hier_in("measurements") self.input_rate = input_rate self.osr = osr @@ -40,6 +41,7 @@ class receiver_hier(gr.hier_block2): self.connect(self, self.filtr, self.interpolator, self.receiver, self) # self.connect(self, self.interpolator, self.receiver, self) self.msg_connect(self.receiver, "bursts", weakref.proxy(self), "bursts") + self.msg_connect(self.receiver, "measurements", weakref.proxy(self), "measurements") def _set_filter(self): filter_cutoff = 125e3 -- cgit v1.2.3