diff options
author | Elof Wecksell <elof@wecksell.se> | 2021-01-12 10:35:31 +0100 |
---|---|---|
committer | Martin Braun <martin@gnuradio.org> | 2021-01-29 03:31:03 -0800 |
commit | b16924381c90e07d5d8cf5bc236c7dc48a0fd677 (patch) | |
tree | e5fe0c4005af6c6f0a6178139439a478de0abacf /gr-digital/python/digital | |
parent | 761b327c761e0596f72cbe9f63e510f1b36c4b47 (diff) |
gr-digital: improved performance and capability of scramblers to 64-bit registers. Use __builtin_parity or volk popcnt. qa was also enhanced to detect errors.
Signed-off-by: Elof Wecksell <elof@wecksell.se>
Co-authored-by: Martin Braun <martin@gnuradio.org>
Diffstat (limited to 'gr-digital/python/digital')
13 files changed, 138 insertions, 24 deletions
diff --git a/gr-digital/python/digital/CMakeLists.txt b/gr-digital/python/digital/CMakeLists.txt index b425e4dc72..5dc40f8e3a 100644 --- a/gr-digital/python/digital/CMakeLists.txt +++ b/gr-digital/python/digital/CMakeLists.txt @@ -37,6 +37,7 @@ GR_PYTHON_INSTALL( FILES utils/__init__.py utils/gray_code.py + utils/lfsr.py utils/mod_codes.py utils/alignment.py utils/tagged_streams.py diff --git a/gr-digital/python/digital/bindings/additive_scrambler_bb_python.cc b/gr-digital/python/digital/bindings/additive_scrambler_bb_python.cc index 8e25963e31..76bbe87eaf 100644 --- a/gr-digital/python/digital/bindings/additive_scrambler_bb_python.cc +++ b/gr-digital/python/digital/bindings/additive_scrambler_bb_python.cc @@ -13,8 +13,8 @@ /* If manual edits are made, the following tags should be modified accordingly. */ /* BINDTOOL_GEN_AUTOMATIC(0) */ /* BINDTOOL_USE_PYGCCXML(0) */ -/* BINDTOOL_HEADER_FILE(additive_scrambler_bb.h) */ -/* BINDTOOL_HEADER_FILE_HASH(a35bcdbbc4e320dd37c13a0adb156c57) */ +/* BINDTOOL_HEADER_FILE(additive_scrambler_bb.h) */ +/* BINDTOOL_HEADER_FILE_HASH(526b44ff08fc8ae301bc1e7a3508c540) */ /***********************************************************************************/ #include <pybind11/complex.h> diff --git a/gr-digital/python/digital/bindings/descrambler_bb_python.cc b/gr-digital/python/digital/bindings/descrambler_bb_python.cc index 2f1aa8dccc..6d8d4344a6 100644 --- a/gr-digital/python/digital/bindings/descrambler_bb_python.cc +++ b/gr-digital/python/digital/bindings/descrambler_bb_python.cc @@ -13,8 +13,8 @@ /* If manual edits are made, the following tags should be modified accordingly. */ /* BINDTOOL_GEN_AUTOMATIC(0) */ /* BINDTOOL_USE_PYGCCXML(0) */ -/* BINDTOOL_HEADER_FILE(descrambler_bb.h) */ -/* BINDTOOL_HEADER_FILE_HASH(d630c5081c027a198e21e0622963b40b) */ +/* BINDTOOL_HEADER_FILE(descrambler_bb.h) */ +/* BINDTOOL_HEADER_FILE_HASH(af9fc52c6846ef88340101d3c6bbacc7) */ /***********************************************************************************/ #include <pybind11/complex.h> diff --git a/gr-digital/python/digital/bindings/glfsr_python.cc b/gr-digital/python/digital/bindings/glfsr_python.cc index e716305931..4f120b0e1e 100644 --- a/gr-digital/python/digital/bindings/glfsr_python.cc +++ b/gr-digital/python/digital/bindings/glfsr_python.cc @@ -13,8 +13,8 @@ /* If manual edits are made, the following tags should be modified accordingly. */ /* BINDTOOL_GEN_AUTOMATIC(0) */ /* BINDTOOL_USE_PYGCCXML(0) */ -/* BINDTOOL_HEADER_FILE(glfsr.h) */ -/* BINDTOOL_HEADER_FILE_HASH(4299ef7303d0f404e97b16abb6de32ee) */ +/* BINDTOOL_HEADER_FILE(glfsr.h) */ +/* BINDTOOL_HEADER_FILE_HASH(0a9a03db1fc3d6a7b81b009e620c41c2) */ /***********************************************************************************/ #include <pybind11/complex.h> @@ -35,7 +35,7 @@ void bind_glfsr(py::module& m) py::class_<glfsr, std::shared_ptr<glfsr>>(m, "glfsr", D(glfsr)) - .def(py::init<uint32_t, uint32_t>(), + .def(py::init<uint64_t, uint64_t>(), py::arg("mask"), py::arg("seed"), D(glfsr, glfsr, 0)) diff --git a/gr-digital/python/digital/bindings/glfsr_source_b_python.cc b/gr-digital/python/digital/bindings/glfsr_source_b_python.cc index 5c8a850f41..17b3e7636f 100644 --- a/gr-digital/python/digital/bindings/glfsr_source_b_python.cc +++ b/gr-digital/python/digital/bindings/glfsr_source_b_python.cc @@ -13,8 +13,8 @@ /* If manual edits are made, the following tags should be modified accordingly. */ /* BINDTOOL_GEN_AUTOMATIC(0) */ /* BINDTOOL_USE_PYGCCXML(0) */ -/* BINDTOOL_HEADER_FILE(glfsr_source_b.h) */ -/* BINDTOOL_HEADER_FILE_HASH(7b6d09b85b54ce73f4e3c80b1809bed7) */ +/* BINDTOOL_HEADER_FILE(glfsr_source_b.h) */ +/* BINDTOOL_HEADER_FILE_HASH(d5e395b769198beb82a644f02b11f443) */ /***********************************************************************************/ #include <pybind11/complex.h> diff --git a/gr-digital/python/digital/bindings/glfsr_source_f_python.cc b/gr-digital/python/digital/bindings/glfsr_source_f_python.cc index 4e8a9883a0..7926ae0e09 100644 --- a/gr-digital/python/digital/bindings/glfsr_source_f_python.cc +++ b/gr-digital/python/digital/bindings/glfsr_source_f_python.cc @@ -13,8 +13,8 @@ /* If manual edits are made, the following tags should be modified accordingly. */ /* BINDTOOL_GEN_AUTOMATIC(0) */ /* BINDTOOL_USE_PYGCCXML(0) */ -/* BINDTOOL_HEADER_FILE(glfsr_source_f.h) */ -/* BINDTOOL_HEADER_FILE_HASH(fefa5e006f5c5c49a91ff379b1c571d4) */ +/* BINDTOOL_HEADER_FILE(glfsr_source_f.h) */ +/* BINDTOOL_HEADER_FILE_HASH(2e85e8ef0b1f9d6585364f2342f14ad1) */ /***********************************************************************************/ #include <pybind11/complex.h> diff --git a/gr-digital/python/digital/bindings/lfsr_python.cc b/gr-digital/python/digital/bindings/lfsr_python.cc index 8efe61e005..c971d77f46 100644 --- a/gr-digital/python/digital/bindings/lfsr_python.cc +++ b/gr-digital/python/digital/bindings/lfsr_python.cc @@ -13,8 +13,8 @@ /* If manual edits are made, the following tags should be modified accordingly. */ /* BINDTOOL_GEN_AUTOMATIC(0) */ /* BINDTOOL_USE_PYGCCXML(0) */ -/* BINDTOOL_HEADER_FILE(lfsr.h) */ -/* BINDTOOL_HEADER_FILE_HASH(6ad566b46874d5852d96814797e67bc4) */ +/* BINDTOOL_HEADER_FILE(lfsr.h) */ +/* BINDTOOL_HEADER_FILE_HASH(ae0c3aeb06a21a60a4415fe89135120e) */ /***********************************************************************************/ #include <pybind11/complex.h> @@ -35,7 +35,7 @@ void bind_lfsr(py::module& m) py::class_<lfsr, std::shared_ptr<lfsr>>(m, "lfsr", D(lfsr)) - .def(py::init<uint32_t, uint32_t, uint32_t>(), + .def(py::init<uint64_t, uint64_t, uint32_t>(), py::arg("mask"), py::arg("seed"), py::arg("reg_len"), diff --git a/gr-digital/python/digital/bindings/scrambler_bb_python.cc b/gr-digital/python/digital/bindings/scrambler_bb_python.cc index 6632e97261..1afaa9e2e1 100644 --- a/gr-digital/python/digital/bindings/scrambler_bb_python.cc +++ b/gr-digital/python/digital/bindings/scrambler_bb_python.cc @@ -13,8 +13,8 @@ /* If manual edits are made, the following tags should be modified accordingly. */ /* BINDTOOL_GEN_AUTOMATIC(0) */ /* BINDTOOL_USE_PYGCCXML(0) */ -/* BINDTOOL_HEADER_FILE(scrambler_bb.h) */ -/* BINDTOOL_HEADER_FILE_HASH(857ba8ebaf0d0a1080493edc410b3aea) */ +/* BINDTOOL_HEADER_FILE(scrambler_bb.h) */ +/* BINDTOOL_HEADER_FILE_HASH(83d0f3673216d2c2d68b1833512e01e2) */ /***********************************************************************************/ #include <pybind11/complex.h> diff --git a/gr-digital/python/digital/qa_glfsr_source.py b/gr-digital/python/digital/qa_glfsr_source.py index dbe488399b..7fde483ed3 100644 --- a/gr-digital/python/digital/qa_glfsr_source.py +++ b/gr-digital/python/digital/qa_glfsr_source.py @@ -29,7 +29,7 @@ class test_glfsr_source(gr_unittest.TestCase): self.assertRaises(RuntimeError, lambda: digital.glfsr_source_b(0)) self.assertRaises(RuntimeError, - lambda: digital.glfsr_source_b(33)) + lambda: digital.glfsr_source_b(65)) def test_002_correlation_b(self): for degree in range( @@ -59,8 +59,8 @@ class test_glfsr_source(gr_unittest.TestCase): self.assertRaises(RuntimeError, lambda: digital.glfsr_source_f(0)) self.assertRaises(RuntimeError, - lambda: digital.glfsr_source_f(33)) - + lambda: digital.glfsr_source_f(65)) + def test_005_correlation_f(self): for degree in range( 1, 11): # Higher degrees take too long to correlate diff --git a/gr-digital/python/digital/qa_lfsr.py b/gr-digital/python/digital/qa_lfsr.py index 0d7a2ccb94..e0db6a382b 100644 --- a/gr-digital/python/digital/qa_lfsr.py +++ b/gr-digital/python/digital/qa_lfsr.py @@ -10,9 +10,9 @@ import math - +import numpy as np from gnuradio import gr, gr_unittest, digital - +from gnuradio.digital.utils import lfsr_args class test_lfsr(gr_unittest.TestCase): @@ -25,7 +25,6 @@ class test_lfsr(gr_unittest.TestCase): def test_lfsr_001(self): reglen = 8 l = digital.lfsr(1, 1, reglen) - result_data = [] for i in range(4 * (reglen + 1)): result_data.append(l.next_bit()) @@ -33,6 +32,20 @@ class test_lfsr(gr_unittest.TestCase): expected_result = 4 * ([1, ] + reglen * [0, ]) self.assertFloatTuplesAlmostEqual(expected_result, result_data, 5) + def test_lfsr_002(self): + l = digital.lfsr(*lfsr_args(0b1,5,3,0)) + result_data = [l.next_bit() for _ in range(2*(2**5-1))] + + expected_result = [1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, + 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0]*2 + self.assertEqual(expected_result, result_data) + + seq1 = [l.next_bit() for _ in range(2**5-1)] + seq2 = [l.next_bit() for _ in range(2**5-1)] + self.assertEqual(seq1,seq2) + + res = (np.convolve(seq1,[1,0,1,0,0,1])%2) + self.assertTrue(sum(res[5:-5])==0,"LRS not generated properly") if __name__ == '__main__': gr_unittest.run(test_lfsr) diff --git a/gr-digital/python/digital/qa_scrambler.py b/gr-digital/python/digital/qa_scrambler.py index 6f6c35134a..7d87f87b6c 100644 --- a/gr-digital/python/digital/qa_scrambler.py +++ b/gr-digital/python/digital/qa_scrambler.py @@ -8,8 +8,9 @@ # # - from gnuradio import gr, gr_unittest, digital, blocks +from gnuradio.digital.utils import lfsr_args +import numpy as np import pmt # See gr-digital/lib/additive_scrambler_bb_impl.cc for reference. @@ -25,7 +26,6 @@ def additive_scramble_lfsr(mask, seed, reglen, bpb, data): out.append(d ^ scramble_word) return out - class test_scrambler(gr_unittest.TestCase): def setUp(self): @@ -34,6 +34,83 @@ class test_scrambler(gr_unittest.TestCase): def tearDown(self): self.tb = None + + def test_lfsr_002(self): + _a = lfsr_args(1,51,3,0) + l = digital.lfsr(*_a) + seq = [l.next_bit() for _ in range(2**10)] + reg = np.zeros(52,np.int8) + reg[::-1][(51,3,0),] = 1 + res = (np.convolve(seq,reg)%2) + self.assertTrue(sum(res[52:-52])==0,"LRS not generated properly") + + def test_scrambler_descrambler_001(self): + src_data = np.random.randint(0,2,500,dtype=np.int8) + src = blocks.vector_source_b(src_data, False) + scrambler = digital.scrambler_bb(*lfsr_args(0b1,7,2,0)) # p(x) = x^7 + x^2 + 1 + descrambler = digital.descrambler_bb(*lfsr_args(0b111,7,2,0)) # we can use any seed here, it is descrambling. + m_tap = blocks.vector_sink_b() + dst = blocks.vector_sink_b() + self.tb.connect(src, scrambler, descrambler, dst) + self.tb.connect(scrambler, m_tap) + self.tb.run() + self.assertEqual(src_data[:-7].tolist(), dst.data()[7:]) # skip garbage during synchronization + self.assertEqual(tuple(np.convolve(m_tap.data(),[1,0,0,0,0,1,0,1])%2)[7:-10], + tuple(src_data[:-10])) # manual descrambling test + + def test_scrambler_descrambler_002(self): + _a = lfsr_args(0b1,51,6,0) #p(x) = x^51+x^6+1 + src_data = np.random.randint(0,2,1000,dtype=np.int8) + src = blocks.vector_source_b(src_data, False) + scrambler = digital.scrambler_bb(*_a) + m_tap = blocks.vector_sink_b() + descrambler = digital.descrambler_bb(*_a) + dst = blocks.vector_sink_b() + self.tb.connect(src, scrambler, descrambler, dst) + self.tb.connect(scrambler, m_tap) + self.tb.run() + self.assertTrue(np.all(src_data[:-51]==dst.data()[51:])) # skip garbage during synchronization + reg = np.zeros(52,np.int8) + reg[::-1][(51,6,0),] = 1 + self.assertTrue(np.all( np.convolve(m_tap.data(),reg)[51:-60]%2 + == src_data[:-60])) # manual descrambling test + + def test_scrambler_descrambler_003(self): + src_data = np.random.randint(0,2,1000,dtype=np.int8) + src = blocks.vector_source_b(src_data, False) + scrambler = digital.scrambler_bb(*lfsr_args(1,12,10,3,2,0)) # this is the product of the other two + descrambler1 = digital.descrambler_bb(*lfsr_args(1,5,3,0)) + descrambler2 = digital.descrambler_bb(*lfsr_args(1,7,2,0)) + dst = blocks.vector_sink_b() + self.tb.connect(src, scrambler, descrambler1, descrambler2, dst) + self.tb.run() + self.assertTrue(np.all(src_data[:-12]==dst.data()[12:])) # skip garbage during synchronization + + def test_additive_scrambler_001(self): + _a = lfsr_args(1,51,3,0) #i p(x) = x^51+x^3+1, seed 0x1 + src_data = np.random.randint(0,2,1000,dtype=np.int8).tolist() + src = blocks.vector_source_b(src_data, False) + scrambler = digital.additive_scrambler_bb(*_a) + descrambler = digital.additive_scrambler_bb(*_a) + dst = blocks.vector_sink_b() + self.tb.connect(src, scrambler, descrambler, dst) + self.tb.run() + self.assertEqual(tuple(src_data), tuple(dst.data())) + + def test_additive_scrambler_002(self): + _a = lfsr_args(1,51,3,0) #i p(x) = x^51+x^3+1, seed 0x1 + src_data = [1,]*1000 + src = blocks.vector_source_b(src_data, False) + scrambler = digital.additive_scrambler_bb(*_a) + dst = blocks.vector_sink_b() + self.tb.connect(src, scrambler, dst) + self.tb.run() + reg = np.zeros(52,np.int8) + reg[::-1][(51,3,0),] = 1 + res = (np.convolve(dst.data(),reg)%2)[52:-52] + self.assertEqual(len(res), sum(res)) # when convolved with mask, + # after sync, only 1's would be returned. + def test_scrambler_descrambler(self): src_data = [1, ] * 1000 src = blocks.vector_source_b(src_data, False) diff --git a/gr-digital/python/digital/utils/__init__.py b/gr-digital/python/digital/utils/__init__.py index 47378f54c5..0d5aae0b79 100644 --- a/gr-digital/python/digital/utils/__init__.py +++ b/gr-digital/python/digital/utils/__init__.py @@ -7,3 +7,5 @@ # SPDX-License-Identifier: GPL-3.0-or-later # # + +from .lfsr import lfsr_args diff --git a/gr-digital/python/digital/utils/lfsr.py b/gr-digital/python/digital/utils/lfsr.py new file mode 100644 index 0000000000..2b8a47cb76 --- /dev/null +++ b/gr-digital/python/digital/utils/lfsr.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python +# +# Copyright 2020 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# SPDX-License-Identifier: GPL-3.0-or-later +# +# + +def lfsr_args(seed, *exp): + """ + Produce arguments to create scrambler objects from exponent polynomial expressions. + seed: start-value of register + *exp: exponents of desired polynomial. + Example: + >>> l = digital.lfsr(*lfrs_args(0b11001,7,1,0)) + Creates an lfsr object with seed 0b11001, mask 0b1000011, K=6 + """ + from functools import reduce + return reduce(int.__xor__, map(lambda x:2**x, exp)), seed, max(exp)-1 |