From f1f3708dfa3cdd164e74a58a4fa56061bf6669a0 Mon Sep 17 00:00:00 2001 From: Christoph Mayer Date: Tue, 27 Aug 2019 08:49:07 +0200 Subject: [PATCH] intermediate --- grc/CMakeLists.txt | 1 + grc/digitalhf_cis_12_channelizer.xml | 32 +++++++ include/digitalhf/CMakeLists.txt | 1 + include/digitalhf/vector_pll_cc.h | 59 ++++++++++++ lib/CMakeLists.txt | 3 +- lib/vector_pll_cc_impl.cc | 131 +++++++++++++++++++++++++++ lib/vector_pll_cc_impl.h | 63 +++++++++++++ python/CMakeLists.txt | 1 + python/__init__.py | 1 + python/cis_12_channelizer.py | 119 ++++++++++++++++++++++++ python/qa_viterbi27_punct.py | 53 +++++++++++ python/qa_viterbi27_tailbiting.py | 100 ++++++++++++++++++++ swig/digitalhf_swig.i | 4 + 13 files changed, 567 insertions(+), 1 deletion(-) create mode 100644 grc/digitalhf_cis_12_channelizer.xml create mode 100644 include/digitalhf/vector_pll_cc.h create mode 100644 lib/vector_pll_cc_impl.cc create mode 100644 lib/vector_pll_cc_impl.h create mode 100644 python/cis_12_channelizer.py create mode 100644 python/qa_viterbi27_punct.py create mode 100644 python/qa_viterbi27_tailbiting.py diff --git a/grc/CMakeLists.txt b/grc/CMakeLists.txt index e01edda..a37bd91 100644 --- a/grc/CMakeLists.txt +++ b/grc/CMakeLists.txt @@ -21,5 +21,6 @@ install(FILES digitalhf_adaptive_dfe.xml digitalhf_physical_layer_driver.xml digitalhf_doppler_correction_cc.xml + digitalhf_cis_12_channelizer.xml digitalhf_msg_proxy.xml DESTINATION share/gnuradio/grc/blocks ) diff --git a/grc/digitalhf_cis_12_channelizer.xml b/grc/digitalhf_cis_12_channelizer.xml new file mode 100644 index 0000000..871e380 --- /dev/null +++ b/grc/digitalhf_cis_12_channelizer.xml @@ -0,0 +1,32 @@ + + + cis_12_channelizer + digitalhf_cis_12_channelizer + [digitalhf] + import digitalhf + digitalhf.cis_12_channelizer() + + + + in + complex + + + + out1 + complex + + + out2 + complex + + diff --git a/include/digitalhf/CMakeLists.txt b/include/digitalhf/CMakeLists.txt index 64f9c7c..18d04e0 100644 --- a/include/digitalhf/CMakeLists.txt +++ b/include/digitalhf/CMakeLists.txt @@ -29,5 +29,6 @@ install(FILES viterbi29.h viterbi39.h viterbi48.h + vector_pll_cc.h DESTINATION include/digitalhf ) diff --git a/include/digitalhf/vector_pll_cc.h b/include/digitalhf/vector_pll_cc.h new file mode 100644 index 0000000..fc97a87 --- /dev/null +++ b/include/digitalhf/vector_pll_cc.h @@ -0,0 +1,59 @@ +/* -*- c++ -*- */ +/* + * Copyright 2018 hcab14@gmail.com. + * + * 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_DIGITALHF_VECTOR_PLL_CC_H +#define INCLUDED_DIGITALHF_VECTOR_PLL_CC_H + +#include +#include + +namespace gr { +namespace digitalhf { + +/*! + * \brief <+description of block+> + * \ingroup digitalhf + * + */ +class DIGITALHF_API vector_pll_cc : virtual public gr::sync_block +{ + public: + typedef boost::shared_ptr sptr; + + /*! + * \brief Return a shared_ptr to a new instance of digitalhf::vector_pll_cc. + * + * To avoid accidental use of raw pointers, digitalhf::vector_pll_cc's + * constructor is in a private implementation + * class. digitalhf::vector_pll_cc::make is the public interface for + * creating new instances. + */ + static sptr make(float samp_rate, + size_t order, + std::vector freq_multipliers); + +}; + +} // namespace digitalhf +} // namespace gr + +#endif /* INCLUDED_DIGITALHF_VECTOR_PLL_CC_H */ + diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index c32e57d..a08ad7e 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -31,7 +31,8 @@ list(APPEND digitalhf_sources viterbi27_impl.cc viterbi29_impl.cc viterbi39_impl.cc - viterbi48_impl.cc) + viterbi48_impl.cc + vector_pll_cc_impl.cc) set(digitalhf_sources "${digitalhf_sources}" PARENT_SCOPE) if(NOT digitalhf_sources) diff --git a/lib/vector_pll_cc_impl.cc b/lib/vector_pll_cc_impl.cc new file mode 100644 index 0000000..31fdb21 --- /dev/null +++ b/lib/vector_pll_cc_impl.cc @@ -0,0 +1,131 @@ +/* -*- c++ -*- */ +/* + * Copyright 2018 hcab14@gmail.com. + * + * 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 +#include +#include + +#include "vector_pll_cc_impl.h" + +namespace gr { +namespace digitalhf { + +vector_pll_cc::sptr +vector_pll_cc::make(float samp_rate, + size_t order, + std::vector freq_multipliers) +{ + return gnuradio::get_initial_sptr + (new vector_pll_cc_impl(samp_rate, + order, + freq_multipliers)); +} + +/* + * The private constructor + */ +vector_pll_cc_impl::vector_pll_cc_impl(float samp_rate, + size_t order, + std::vector freq_multipliers) + : gr::sync_block("vector_pll_cc", + gr::io_signature::make(1, 1, freq_multipliers.size() * sizeof(gr_complex)), + gr::io_signature::make(1, 1, freq_multipliers.size() * sizeof(gr_complex))) + , _ts(1.0/samp_rate) + , _order(order) + , _freq_multipliers(freq_multipliers) + , _control_loop(6.28/600.0, -6.28/600.0*10, 6.28/600.0*10) + , _theta (freq_multipliers.size(), 0.0f) + , _dtheta(freq_multipliers.size(), 0.0f) + , _absz(freq_multipliers.size(), 1.0f) + , _b({+0.6284830097698858, + -0.6281540229565162}) + , _uf(0) + , _ud(0) +{ + GR_LOG_DECLARE_LOGPTR(d_logger); + GR_LOG_ASSIGN_LOGPTR(d_logger, "vector_pll_cc"); + for (int i=0; i<_freq_multipliers.size(); ++i) { + std::cout << "FM: " << i <<" " << _freq_multipliers[i] << std::endl; + } +} + +vector_pll_cc_impl::~vector_pll_cc_impl() +{ +} + +int +vector_pll_cc_impl::work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) +{ + gr::thread::scoped_lock lock(d_setlock); + + size_t const vlen = _freq_multipliers.size(); + const gr_complex *in = (const gr_complex*)input_items[0]; + gr_complex *out = (gr_complex*)output_items[0]; + _control_loop.frequency_limit(); + + std::vector error(vlen); + + float const mu = 5e-2; + + for (int i=0; i + +#include + +#include + +namespace gr { +namespace digitalhf { + +class vector_pll_cc_impl : public vector_pll_cc +{ +private: + float _ts; // sample time (sec) + size_t _order; // + std::vector _freq_multipliers; + gr::blocks::control_loop _control_loop; + std::vector _theta; + std::vector _dtheta; + std::vector _absz; + std::array _b; + float _uf; + float _ud; +public: + vector_pll_cc_impl(float samp_rate, + size_t order, + std::vector freq_multipliers); + virtual ~vector_pll_cc_impl(); + + int work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); + +protected: + +}; + +} // namespace digitalhf +} // namespace gr + +#endif /* INCLUDED_DIGITALHF_VECTOR_PLL_CC_IMPL_H */ diff --git a/python/CMakeLists.txt b/python/CMakeLists.txt index cacf591..841efde 100644 --- a/python/CMakeLists.txt +++ b/python/CMakeLists.txt @@ -33,6 +33,7 @@ GR_PYTHON_INSTALL( FILES __init__.py physical_layer_driver.py + cis_12_channelizer.py msg_proxy.py DESTINATION ${GR_PYTHON_DIR}/digitalhf ) diff --git a/python/__init__.py b/python/__init__.py index 5c912f8..86533f7 100644 --- a/python/__init__.py +++ b/python/__init__.py @@ -33,4 +33,5 @@ except ImportError: # import any pure python here from .physical_layer_driver import physical_layer_driver from .msg_proxy import msg_proxy +from .cis_12_channelizer import cis_12_channelizer # diff --git a/python/cis_12_channelizer.py b/python/cis_12_channelizer.py new file mode 100644 index 0000000..e2e33e1 --- /dev/null +++ b/python/cis_12_channelizer.py @@ -0,0 +1,119 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Copyright 2019 hcab14@gmail.com. +# +# 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. +# + +import importlib +from gnuradio import analog +from gnuradio import blocks +from gnuradio import digital +from gnuradio import filter +from gnuradio.filter import firdes +from gnuradio.filter import pfb +from gnuradio import gr +import pmt +import digitalhf +import numpy as np + +class cis_12_channelizer(gr.hier_block2): + """ + docstring for block CIS-12 channelizer + """ + def __init__(self): + gr.hier_block2.__init__(self, + "cis_12_channelizer", + gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature + gr.io_signature(2, 2, gr.sizeof_gr_complex)) # Output signature + + self.samp_rate = samp_rate = 12000.0 + self.analog_pll_refout_cc_0 = analog.pll_refout_cc(2*np.pi/1000, + 2*np.pi*3270/samp_rate, + 2*np.pi*3330/samp_rate) + taps_pfb = firdes.low_pass_2(1, + self.samp_rate, + 0.45*self.samp_rate/60, + 0.05*self.samp_rate/60, + attenuation_dB=100, + window=filter.firdes.WIN_BLACKMAN_HARRIS) + taps_3300 = firdes.band_pass_2(1, + self.samp_rate, + 3200, + 3400, + 100, + attenuation_dB=60, + window=filter.firdes.WIN_BLACKMAN_HARRIS) + print('N=', len(taps_3300)) + self.filter_fir_filter_ccf_0 = filter.fir_filter_ccf(1,taps_3300) + + self.blocks_multiply_conjugate_cc_0 = blocks.multiply_conjugate_cc(1) + self.blocks_delay_0 = blocks.delay(gr.sizeof_gr_complex, + (len(taps_3300)-1) // 2) + + self.pfb_channelizer_ccf_0 = pfb.channelizer_ccf( + 60, + (taps_pfb), + 1.0, + 100) + chmap = 60-(2+np.arange(12)) + self.pfb_channelizer_ccf_0.set_channel_map((chmap.tolist())) + self.pfb_channelizer_ccf_0.set_channel_map(([47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58])) + self.pfb_channelizer_ccf_0.declare_sample_delay(0) + + self.blocks_streams_to_vector_0 = blocks.streams_to_vector(gr.sizeof_gr_complex, 12) + self.blocks_streams_to_vector_1 = blocks.streams_to_vector(gr.sizeof_gr_complex, 48) + self.blocks_vector_to_stream_0 = blocks.vector_to_stream(gr.sizeof_gr_complex, 12) + + self.blocks_vector_pll_cc_0 = digitalhf.vector_pll_cc(samp_rate = 600.0, + order = 2, + freq_multipliers = [13,12,11,10,9,8,7,6,5,4,3,2]) + + self.blocks_resamplers = [[] for _ in range(12)] + for i in range(12): + self.blocks_resamplers[i] = filter.rational_resampler_ccf(3,1) + + self.connect((self, 0), + (self.filter_fir_filter_ccf_0, 0), + (self.analog_pll_refout_cc_0, 0), + (self.blocks_multiply_conjugate_cc_0, 1)) + self.connect((self, 0), + (self.blocks_delay_0), + (self.blocks_multiply_conjugate_cc_0, 0)) + self.connect((self.blocks_multiply_conjugate_cc_0, 0), + (self.pfb_channelizer_ccf_0, 0)) + self.connect((self.blocks_multiply_conjugate_cc_0, 0), (self, 1)) + #self.connect((self.filter_fir_filter_ccf_0, 0), + # (self, 1)) + + for i in range(12): + self.connect((self.pfb_channelizer_ccf_0, i), + (self.blocks_resamplers[i], 0), + (self.blocks_streams_to_vector_0, i)) + + self.blocks_null_sink_0 = blocks.null_sink(48*gr.sizeof_gr_complex) + for i in range(48): + self.connect((self.pfb_channelizer_ccf_0, 12+i), (self.blocks_streams_to_vector_1, i)) + self.connect(self.blocks_streams_to_vector_1, self.blocks_null_sink_0) + + self.connect(self.blocks_streams_to_vector_0, + self.blocks_vector_pll_cc_0, + self.blocks_vector_to_stream_0, + self) + #self.connect((self,0), + # (self.analog_pll_refout_cc_0, 0), + # (self,0)) diff --git a/python/qa_viterbi27_punct.py b/python/qa_viterbi27_punct.py new file mode 100644 index 0000000..633e7de --- /dev/null +++ b/python/qa_viterbi27_punct.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Copyright 2018 hcab14@mail.com. +# +# 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. +# + +import digitalhf.digitalhf_swig as digitalhf +import random +import pmt +import numpy as np +def main(): + v = digitalhf.viterbi27(0x6d, 0x4f) + N = 35*500+5 + bits = [random.randint(0,1) for _ in range(N+7)] + a = [1,0,1,1,0,1,1] + b = [1,1,1,1,0,0,1] + llr_encoded_bits = [] + for i in range(N): + t1=t2=0 + for j in range(7): + t1 += a[j]*bits[i+7-j] + t2 += b[j]*bits[i+7-j] + llr_encoded_bits.extend([7*(1-2*(t1%2)), 7*(1-2*(t2%2))]) + v.reset() + llr_encoded_bits = np.array(llr_encoded_bits) + llr_encoded_bits[3::6] = 0 + llr_encoded_bits[4::6] = 0 + decoded_bits = v.udpate(llr_encoded_bits) + print(bits[7:37]) + print(decoded_bits[0:30]) + print('quality:', v.quality()*1.2/2/N) + test = [all([decoded_bits[i] == bits[i+7] for i in range(N)]), abs(v.quality()*1.2-2*N)<1] + print('test:', test) + if not all(test): + raise Exception(test) + +if __name__ == '__main__': + main() diff --git a/python/qa_viterbi27_tailbiting.py b/python/qa_viterbi27_tailbiting.py new file mode 100644 index 0000000..1755d0c --- /dev/null +++ b/python/qa_viterbi27_tailbiting.py @@ -0,0 +1,100 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Copyright 2018 hcab14@mail.com. +# +# 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. +# + +import digitalhf.digitalhf_swig as digitalhf +import pmt +import numpy as np + + +def bit2llr(b): + return 7.0*(1 - 2*b) + +class ViterbiEncoder(object): + def __init__(self, k, taps): + self._state = np.zeros(k, dtype=np.uint16) + self._taps = taps + + def encode(self, bit): + self._state = np.roll(self._state, 1) + self._state[0] = bit + return [bit2llr(self._state.dot(tap) % 2) for tap in self._taps] + +def run_test(N, data): + decoder = data['dec'](*data['polys']) + encoder = ViterbiEncoder(data['k'], data['taps']) + k = data['k'] + for p,t in zip(data['polys'], data['taps']): + _t = [b=='1' for b in np.binary_repr(p,k)] + test = _t == t[::-1] + if not test: + raise Exception('inconsistent taps', _t, t[::-1]) + + np.random.seed(123) + bits = np.random.randint(2, size=N) + M = len(data['polys']) + llr_encoded_bits = np.zeros(M*N, dtype=np.float64) + + for i in range(N-k+1,N): + encoder.encode(bits[i]) + for i in range(0,N): + llr_encoded_bits[M*i:M*(i+1)] = encoder.encode(bits[i]) + + print(llr_encoded_bits[:14]) + llr_encoded_bits[3::4] = 0 + decoded_bits = np.roll(decoder.udpate(llr_encoded_bits), 0) + print(decoded_bits.tolist()) + print(bits.tolist()) + print('quality:', decoder.quality(), 100*4/3.5*decoder.quality()/M/N) + test = [np.all(decoded_bits == bits), abs(4/3.5*decoder.quality()-M*N)<1] + print('test:', test) + if not all(test): + raise Exception(test) + +def main(): + data = [{'dec' : digitalhf.viterbi27, + 'polys': [0x6D, 0x4F], + 'k' : 7, + 'taps' : [[1,0,1,1,0,1,1], + [1,1,1,1,0,0,1]]}, + {'dec' : digitalhf.viterbi29, + 'polys': [0x11d, 0x1af], + 'k' : 9, + 'taps' : [[1,0,1,1,1,0,0,0,1], + [1,1,1,1,0,1,0,1,1]]}, + {'dec' : digitalhf.viterbi39, + 'polys': [0x127, 0x19B, 0x1ED], + 'k' : 9, + 'taps' : [[1,1,1,0,0,1,0,0,1], + [1,1,0,1,1,0,0,1,1], + [1,0,1,1,0,1,1,1,1]]}, + {'dec' : digitalhf.viterbi48, + 'polys': [0xB9, 0x9D, 0xD3, 0xF7], + 'k' : 8, + 'taps' : [[1,0,0,1,1,1,0,1], + [1,0,1,1,1,0,0,1], + [1,1,0,0,1,0,1,1], + [1,1,1,0,1,1,1,1]]}] + + for d in data: + run_test(45, d) + +if __name__ == '__main__': + main() diff --git a/swig/digitalhf_swig.i b/swig/digitalhf_swig.i index 95bd414..bac2e49 100644 --- a/swig/digitalhf_swig.i +++ b/swig/digitalhf_swig.i @@ -14,6 +14,7 @@ #include "digitalhf/viterbi29.h" #include "digitalhf/viterbi39.h" #include "digitalhf/viterbi48.h" +#include "digitalhf/vector_pll_cc.h" %} %include "digitalhf/adaptive_dfe.h" @@ -22,6 +23,9 @@ GR_SWIG_BLOCK_MAGIC2(digitalhf, adaptive_dfe); %include "digitalhf/doppler_correction_cc.h" GR_SWIG_BLOCK_MAGIC2(digitalhf, doppler_correction_cc); +%include "digitalhf/vector_pll_cc.h" +GR_SWIG_BLOCK_MAGIC2(digitalhf, vector_pll_cc); + /* FIXME */ %include "digitalhf/viterbi27.h" GR_SWIG_BLOCK_MAGIC2(digitalhf, viterbi27);