From 51c504ebb1ca21c5f7071826c1af03e45e391a65 Mon Sep 17 00:00:00 2001
From: Daniel Estévez <daniel@destevez.net>
Date: Sun, 6 Jun 2021 11:25:57 +0200
Subject: digital: Add NRZI option to differential en/decoder
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

This adds an option to the differential encoder an decoder blocks
to perform NRZI encoding and decoding. NRZI only makes sense with
a modulus of 2, so the blocks constructors will throw and exception
if passed nrzi = true and a modulus different from 2.

The GRC blocks handle this by hiding the modulus field if the user
selects NRZI encoding.

A new unit test for the NRZI version of the blocks is added. Besides
checking that encode plus decode gives the original, this test also
compares the C++ implementation results against a Numpy implementation.

Additionally, a faster implementation of differential encoding/
decoding for modulus 2 is included here.

Signed-off-by: Daniel Estévez <daniel@destevez.net>
---
 gr-digital/python/digital/bindings/CMakeLists.txt  |  1 +
 .../digital/bindings/diff_coding_type_python.cc    | 40 ++++++++++++
 .../digital/bindings/diff_decoder_bb_python.cc     |  3 +-
 .../digital/bindings/diff_encoder_bb_python.cc     |  3 +-
 .../docstrings/diff_coding_type_pydoc_template.h   | 15 +++++
 .../python/digital/bindings/python_bindings.cc     |  2 +
 gr-digital/python/digital/qa_diff_encoder_nrzi.py  | 72 ++++++++++++++++++++++
 7 files changed, 134 insertions(+), 2 deletions(-)
 create mode 100644 gr-digital/python/digital/bindings/diff_coding_type_python.cc
 create mode 100644 gr-digital/python/digital/bindings/docstrings/diff_coding_type_pydoc_template.h
 create mode 100755 gr-digital/python/digital/qa_diff_encoder_nrzi.py

(limited to 'gr-digital/python/digital')

diff --git a/gr-digital/python/digital/bindings/CMakeLists.txt b/gr-digital/python/digital/bindings/CMakeLists.txt
index 4d24678ec9..762769419b 100644
--- a/gr-digital/python/digital/bindings/CMakeLists.txt
+++ b/gr-digital/python/digital/bindings/CMakeLists.txt
@@ -32,6 +32,7 @@ list(APPEND digital_python_files
     crc32_bb_python.cc
     decision_feedback_equalizer_python.cc
     descrambler_bb_python.cc
+    diff_coding_type_python.cc
     diff_decoder_bb_python.cc
     diff_encoder_bb_python.cc
     diff_phasor_cc_python.cc
diff --git a/gr-digital/python/digital/bindings/diff_coding_type_python.cc b/gr-digital/python/digital/bindings/diff_coding_type_python.cc
new file mode 100644
index 0000000000..5663b27127
--- /dev/null
+++ b/gr-digital/python/digital/bindings/diff_coding_type_python.cc
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2021 Daniel Estevez <daniel@destevez.net>
+ *
+ * This file is part of GNU Radio
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ *
+ */
+
+/***********************************************************************************/
+/* This file is automatically generated using bindtool and can be manually edited  */
+/* The following lines can be configured to regenerate this file during cmake      */
+/* If manual edits are made, the following tags should be modified accordingly.    */
+/* BINDTOOL_GEN_AUTOMATIC(0)                                                       */
+/* BINDTOOL_USE_PYGCCXML(0)                                                        */
+/* BINDTOOL_HEADER_FILE(diff_coding_type.h)                                        */
+/* BINDTOOL_HEADER_FILE_HASH(d2a67d2eacf4d643b8df9d240a971837)                     */
+/***********************************************************************************/
+
+#include <pybind11/complex.h>
+#include <pybind11/pybind11.h>
+#include <pybind11/stl.h>
+
+namespace py = pybind11;
+
+#include <gnuradio/digital/diff_coding_type.h>
+// pydoc.h is automatically generated in the build directory
+#include <diff_coding_type_pydoc.h>
+
+void bind_diff_coding_type(py::module& m)
+{
+
+
+    py::enum_<::gr::digital::diff_coding_type>(m, "diff_coding_type")
+        .value("DIFF_DIFFERENTIAL", ::gr::digital::DIFF_DIFFERENTIAL) // 0
+        .value("DIFF_NRZI", ::gr::digital::DIFF_NRZI)                 // 1
+        .export_values();
+
+    py::implicitly_convertible<int, ::gr::digital::diff_coding_type>();
+}
diff --git a/gr-digital/python/digital/bindings/diff_decoder_bb_python.cc b/gr-digital/python/digital/bindings/diff_decoder_bb_python.cc
index f7fb6b0943..a5628fb372 100644
--- a/gr-digital/python/digital/bindings/diff_decoder_bb_python.cc
+++ b/gr-digital/python/digital/bindings/diff_decoder_bb_python.cc
@@ -14,7 +14,7 @@
 /* BINDTOOL_GEN_AUTOMATIC(0)                                                       */
 /* BINDTOOL_USE_PYGCCXML(0)                                                        */
 /* BINDTOOL_HEADER_FILE(diff_decoder_bb.h)                                        */
-/* BINDTOOL_HEADER_FILE_HASH(3814f3ba5b3a9425eae5611e1a4876ec)                     */
+/* BINDTOOL_HEADER_FILE_HASH(92f143bcb3f067b3cdf8292a75956d31)                     */
 /***********************************************************************************/
 
 #include <pybind11/complex.h>
@@ -41,6 +41,7 @@ void bind_diff_decoder_bb(py::module& m)
 
         .def(py::init(&diff_decoder_bb::make),
              py::arg("modulus"),
+             py::arg("nrzi") = ::gr::digital::DIFF_DIFFERENTIAL,
              D(diff_decoder_bb, make))
 
 
diff --git a/gr-digital/python/digital/bindings/diff_encoder_bb_python.cc b/gr-digital/python/digital/bindings/diff_encoder_bb_python.cc
index d27c5feec2..817b2e3da1 100644
--- a/gr-digital/python/digital/bindings/diff_encoder_bb_python.cc
+++ b/gr-digital/python/digital/bindings/diff_encoder_bb_python.cc
@@ -14,7 +14,7 @@
 /* BINDTOOL_GEN_AUTOMATIC(0)                                                       */
 /* BINDTOOL_USE_PYGCCXML(0)                                                        */
 /* BINDTOOL_HEADER_FILE(diff_encoder_bb.h)                                        */
-/* BINDTOOL_HEADER_FILE_HASH(e817d23aac894b3563101df2958577de)                     */
+/* BINDTOOL_HEADER_FILE_HASH(866ae99d6fc12353e8d45ba2424bcecd)                     */
 /***********************************************************************************/
 
 #include <pybind11/complex.h>
@@ -41,6 +41,7 @@ void bind_diff_encoder_bb(py::module& m)
 
         .def(py::init(&diff_encoder_bb::make),
              py::arg("modulus"),
+             py::arg("coding") = ::gr::digital::DIFF_DIFFERENTIAL,
              D(diff_encoder_bb, make))
 
 
diff --git a/gr-digital/python/digital/bindings/docstrings/diff_coding_type_pydoc_template.h b/gr-digital/python/digital/bindings/docstrings/diff_coding_type_pydoc_template.h
new file mode 100644
index 0000000000..84c1cb8104
--- /dev/null
+++ b/gr-digital/python/digital/bindings/docstrings/diff_coding_type_pydoc_template.h
@@ -0,0 +1,15 @@
+/*
+ * Copyright 2020 Free Software Foundation, Inc.
+ *
+ * This file is part of GNU Radio
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ *
+ */
+#include "pydoc_macros.h"
+#define D(...) DOC(gr, digital, __VA_ARGS__)
+/*
+  This file contains placeholders for docstrings for the Python bindings.
+  Do not edit! These were automatically extracted during the binding process
+  and will be overwritten during the build process
+ */
diff --git a/gr-digital/python/digital/bindings/python_bindings.cc b/gr-digital/python/digital/bindings/python_bindings.cc
index 830902679d..0eabcf63d1 100644
--- a/gr-digital/python/digital/bindings/python_bindings.cc
+++ b/gr-digital/python/digital/bindings/python_bindings.cc
@@ -42,6 +42,7 @@ void bind_crc32_async_bb(py::module&);
 void bind_crc32_bb(py::module&);
 void bind_decision_feedback_equalizer(py::module&);
 void bind_descrambler_bb(py::module&);
+void bind_diff_coding_type(py::module&);
 void bind_diff_decoder_bb(py::module&);
 void bind_diff_encoder_bb(py::module&);
 void bind_diff_phasor_cc(py::module&);
@@ -143,6 +144,7 @@ PYBIND11_MODULE(digital_python, m)
     bind_crc32_bb(m);
     bind_decision_feedback_equalizer(m);
     bind_descrambler_bb(m);
+    bind_diff_coding_type(m);
     bind_diff_decoder_bb(m);
     bind_diff_encoder_bb(m);
     bind_diff_phasor_cc(m);
diff --git a/gr-digital/python/digital/qa_diff_encoder_nrzi.py b/gr-digital/python/digital/qa_diff_encoder_nrzi.py
new file mode 100755
index 0000000000..1797288099
--- /dev/null
+++ b/gr-digital/python/digital/qa_diff_encoder_nrzi.py
@@ -0,0 +1,72 @@
+#!/usr/bin/env python
+#
+# Copyright 2020-2021 Daniel Estevez <daniel@destevez.net>
+#
+# This file is part of GNU Radio
+#
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+#
+
+from gnuradio import gr, gr_unittest, digital, blocks
+import numpy as np
+
+
+class test_nrzi(gr_unittest.TestCase):
+    def setUp(self):
+        test_size = 256
+        self.data = np.random.randint(0, 2, test_size, dtype='uint8')
+        self.source = blocks.vector_source_b(self.data, False, 1, [])
+        self.sink = blocks.vector_sink_b(1, 0)
+        self.tb = gr.top_block()
+
+    def tearDown(self):
+        self.tb = None
+        del(self.data)
+        del(self.source)
+        del(self.sink)
+
+    def test_encode(self):
+        """Performs NRZI encode and checks the result"""
+        encoder = digital.diff_encoder_bb(2, digital.DIFF_NRZI)
+
+        self.tb.connect(self.source, encoder, self.sink)
+        self.tb.start()
+        self.tb.wait()
+
+        expected = np.cumsum((1 ^ self.data) & 1) & 1
+
+        np.testing.assert_equal(
+            self.sink.data(), expected,
+            'NRZI encode output does not match expected result')
+
+    def test_decode(self):
+        """Performs NRZI decode and checks the result"""
+        decoder = digital.diff_decoder_bb(2, digital.DIFF_NRZI)
+
+        self.tb.connect(self.source, decoder, self.sink)
+        self.tb.start()
+        self.tb.wait()
+
+        expected = self.data[1:] ^ self.data[:-1] ^ 1
+
+        np.testing.assert_equal(
+            self.sink.data()[1:], expected,
+            'NRZI decode output does not match expected result')
+
+    def test_encode_decode(self):
+        """Performs NRZI encode and decode and checks the result"""
+        encoder = digital.diff_encoder_bb(2, digital.DIFF_NRZI)
+        decoder = digital.diff_decoder_bb(2, digital.DIFF_NRZI)
+
+        self.tb.connect(self.source, encoder, decoder, self.sink)
+        self.tb.start()
+        self.tb.wait()
+
+        np.testing.assert_equal(
+            self.sink.data(), self.data,
+            'NRZI encoded and decoded output does not match input')
+
+
+if __name__ == '__main__':
+    gr_unittest.run(test_nrzi)
-- 
cgit v1.2.3