aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--host/lib/convert/gen_convert_general.py73
-rw-r--r--host/tests/convert_test.cpp133
2 files changed, 174 insertions, 32 deletions
diff --git a/host/lib/convert/gen_convert_general.py b/host/lib/convert/gen_convert_general.py
index c4a8c9725..1b1106d4c 100644
--- a/host/lib/convert/gen_convert_general.py
+++ b/host/lib/convert/gen_convert_general.py
@@ -5,6 +5,9 @@
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
+"""
+Auto-Generator for generic converters
+"""
TMPL_HEADER = """
<%
@@ -16,6 +19,7 @@ TMPL_HEADER = """
#include "convert_common.hpp"
#include <uhd/utils/byteswap.hpp>
+#include <algorithm>
using namespace uhd::convert;
@@ -29,6 +33,48 @@ DECLARE_CONVERTER(item32, 1, item32, 1, PRIORITY_GENERAL) {
}
"""
+TMPL_CONV_CHDR_SC16_TO_FP = """
+DECLARE_CONVERTER({fctype}, 1, sc16_chdr, 1, PRIORITY_GENERAL) {{
+ // Note: We convert I and Q separately, because there's no optimized
+ // constructor to create a complex<{fptype}> from a complex<int16_t>. This
+ // means we need to multiply nsamps by 2
+ const {fptype}* input = reinterpret_cast<const {fptype}*>(inputs[0]);
+ int16_t* output = reinterpret_cast<int16_t*>(outputs[0]);
+
+ for (size_t i = 0; i < nsamps * 2; i += 2) {{
+ output[i] = static_cast<int16_t>(input[i] * {fptype}(scale_factor));
+ output[i+1] = static_cast<int16_t>(input[i+1] * {fptype}(scale_factor));
+ }}
+}}
+
+DECLARE_CONVERTER(sc16_chdr, 1, {fctype}, 1, PRIORITY_GENERAL) {{
+ // Note: We convert I and Q separately, because there's no optimized
+ // constructor to create a complex<{fptype}> from a complex<int16_t>. This
+ // means we need to multiply nsamps by 2
+ const int16_t* input = reinterpret_cast<const int16_t*>(inputs[0]);
+ {fptype}* output = reinterpret_cast<{fptype}*>(outputs[0]);
+
+ for (size_t i = 0; i < nsamps * 2; i += 2) {{
+ output[i] = static_cast<{fptype}>(input[i]) * {fptype}(scale_factor);
+ output[i+1] = static_cast<{fptype}>(input[i+1]) * {fptype}(scale_factor);
+ }}
+}}
+"""
+
+# For CHDR converters, all converters where the input and output type are the
+# same can be done by a memcpy, because we can do the endianness adaptation in
+# the FPGA.
+TMPL_CONV_CHDR_MEMCPY = """
+DECLARE_CONVERTER({in_type}, 1, {out_type}, 1, PRIORITY_GENERAL) {{
+ const {ptr_type} *input = reinterpret_cast<const {ptr_type} *>(inputs[0]);
+ {ptr_type}* output = reinterpret_cast<{ptr_type}*>(outputs[0]);
+
+ // Benchmark shows that copy_n can be significantly slower in some cases
+ //std::copy_n(input, nsamps, output);
+ memcpy(output, input, sizeof({ptr_type}) * nsamps);
+}}
+"""
+
# Some 32-bit types converters are also defined in convert_item32.cpp to
# take care of quirks such as I/Q ordering on the wire etc.
TMPL_CONV_ITEM32 = """
@@ -177,10 +223,35 @@ def parse_tmpl(_tmpl_text, **kwargs):
return Template(_tmpl_text).render(**kwargs)
if __name__ == '__main__':
- import sys, os
+ import sys
+ import os
file = os.path.basename(__file__)
output = parse_tmpl(TMPL_HEADER, file=file)
+ for fctype, fptype in (
+ ('fc32', 'float'),
+ ('fc64', 'double'),
+ ):
+ output += TMPL_CONV_CHDR_SC16_TO_FP.format(
+ fctype=fctype, fptype=fptype)
+
+ ## Generate CHDR converters
+ # These guys don't have to worry about endianness
+ for uhd_type, ptr_type in (
+ ('u8', 'uint8_t'),
+ ('s8', 'int8_t'),
+ ('s16', 'int16_t'),
+ ('f32', 'float'),
+ ('sc8', 'std::complex<int8_t>'),
+ ('sc16', 'std::complex<int16_t>'),
+ ('fc32', 'std::complex<float>'),
+ ('fc64', 'std::complex<double>'),
+ ):
+ for in_type, out_type in ((uhd_type + '_chdr', uhd_type), (uhd_type, uhd_type + '_chdr')):
+ output += TMPL_CONV_CHDR_MEMCPY.format(
+ in_type=in_type,
+ out_type=out_type,
+ ptr_type=ptr_type)
## Generate all data types that are exactly
## item32 or multiples thereof:
for end in ('be', 'le'):
diff --git a/host/tests/convert_test.cpp b/host/tests/convert_test.cpp
index 67409e310..9bdfbef6c 100644
--- a/host/tests/convert_test.cpp
+++ b/host/tests/convert_test.cpp
@@ -110,6 +110,20 @@ BOOST_AUTO_TEST_CASE(test_convert_types_le_sc16)
}
}
+BOOST_AUTO_TEST_CASE(test_convert_types_chdr_sc16)
+{
+ convert::id_type id;
+ id.input_format = "sc16";
+ id.num_inputs = 1;
+ id.output_format = "sc16_chdr";
+ id.num_outputs = 1;
+
+ // try various lengths to test edge cases
+ for (size_t nsamps = 1; nsamps < 16; nsamps++) {
+ test_convert_types_sc16(nsamps, id);
+ }
+}
+
/***********************************************************************
* Test float conversion
**********************************************************************/
@@ -175,6 +189,20 @@ BOOST_AUTO_TEST_CASE(test_convert_types_le_fc32)
}
}
+BOOST_AUTO_TEST_CASE(test_convert_types_chdr_fc32)
+{
+ convert::id_type id;
+ id.input_format = "fc32";
+ id.num_inputs = 1;
+ id.output_format = "sc16_chdr";
+ id.num_outputs = 1;
+
+ // try various lengths to test edge cases
+ for (size_t nsamps = 1; nsamps < 16; nsamps++) {
+ test_convert_types_for_floats<fc32_t>(nsamps, id);
+ }
+}
+
BOOST_AUTO_TEST_CASE(test_convert_types_be_fc64)
{
convert::id_type id;
@@ -203,6 +231,20 @@ BOOST_AUTO_TEST_CASE(test_convert_types_le_fc64)
}
}
+BOOST_AUTO_TEST_CASE(test_convert_types_chdr_fc64)
+{
+ convert::id_type id;
+ id.input_format = "fc64";
+ id.num_inputs = 1;
+ id.output_format = "sc16_chdr";
+ id.num_outputs = 1;
+
+ // try various lengths to test edge cases
+ for (size_t nsamps = 1; nsamps < 16; nsamps++) {
+ test_convert_types_for_floats<fc64_t>(nsamps, id);
+ }
+}
+
/***********************************************************************
* Test float to/from sc12 conversion loopback
**********************************************************************/
@@ -294,6 +336,20 @@ BOOST_AUTO_TEST_CASE(test_convert_types_be_fc32_with_fc32)
}
}
+BOOST_AUTO_TEST_CASE(test_convert_types_fc32_with_fc32_chdr)
+{
+ convert::id_type id;
+ id.input_format = "fc32";
+ id.num_inputs = 1;
+ id.output_format = "fc32_chdr";
+ id.num_outputs = 1;
+
+ // try various lengths to test edge cases
+ for (size_t nsamps = 1; nsamps < 16; nsamps++) {
+ test_convert_types_for_floats<fc32_t>(nsamps, id);
+ }
+}
+
/***********************************************************************
* Test float to short conversion loopback
**********************************************************************/
@@ -488,6 +544,20 @@ BOOST_AUTO_TEST_CASE(test_convert_types_u8_and_u8)
}
}
+BOOST_AUTO_TEST_CASE(test_convert_types_u8_and_u8_chdr)
+{
+ convert::id_type id;
+ id.input_format = "u8";
+ id.output_format = "u8_chdr";
+ id.num_inputs = 1;
+ id.num_outputs = 1;
+
+ // try various lengths to test edge cases
+ for (size_t nsamps = 1; nsamps < 16; nsamps++) {
+ test_convert_types_u8(nsamps, id);
+ }
+}
+
/***********************************************************************
* Test s8 conversion
**********************************************************************/
@@ -528,6 +598,20 @@ BOOST_AUTO_TEST_CASE(test_convert_types_s8_and_s8)
}
}
+BOOST_AUTO_TEST_CASE(test_convert_types_s8_and_s8_chdr)
+{
+ convert::id_type id;
+ id.input_format = "s8";
+ id.output_format = "s8_chdr";
+ id.num_inputs = 1;
+ id.num_outputs = 1;
+
+ // try various lengths to test edge cases
+ for (size_t nsamps = 1; nsamps < 16; nsamps++) {
+ test_convert_types_s8(nsamps, id);
+ }
+}
+
/***********************************************************************
* Test s16 conversion
**********************************************************************/
@@ -568,6 +652,20 @@ BOOST_AUTO_TEST_CASE(test_convert_types_s16_and_s16)
}
}
+BOOST_AUTO_TEST_CASE(test_convert_types_s16_and_s16_chdr)
+{
+ convert::id_type id;
+ id.input_format = "s16";
+ id.output_format = "s16_chdr";
+ id.num_inputs = 1;
+ id.num_outputs = 1;
+
+ // try various lengths to test edge cases
+ for (size_t nsamps = 1; nsamps < 16; nsamps++) {
+ test_convert_types_s16(nsamps, id);
+ }
+}
+
/***********************************************************************
* Test fc32 -> fc32 conversion
**********************************************************************/
@@ -611,43 +709,16 @@ BOOST_AUTO_TEST_CASE(test_convert_types_fc32_and_fc32)
}
}
-/***********************************************************************
- * Test f32 -> f32 conversion
- **********************************************************************/
-static void test_convert_types_f32(size_t nsamps, convert::id_type& id)
-{
- // fill the input samples
- std::vector<float> input(nsamps), output(nsamps);
- for (float& in : input)
- in = float((float(std::rand()) / float(RAND_MAX / 2)) - 1);
-
- // run the loopback and test
- convert::id_type in_id = id;
- convert::id_type out_id = id;
- std::swap(out_id.input_format, out_id.output_format);
- std::swap(out_id.num_inputs, out_id.num_outputs);
- loopback(nsamps, in_id, out_id, input, output);
- for (size_t i = 0; i < nsamps; i++) {
- MY_CHECK_CLOSE(input[i], output[i], float(1. / (1 << 16)));
- }
-}
-
-BOOST_AUTO_TEST_CASE(test_convert_types_f32_and_f32)
+BOOST_AUTO_TEST_CASE(test_convert_types_fc32_and_fc32_chdr)
{
convert::id_type id;
- id.input_format = "f32";
+ id.input_format = "fc32";
+ id.output_format = "fc32_chdr";
id.num_inputs = 1;
id.num_outputs = 1;
// try various lengths to test edge cases
- id.output_format = "f32_item32_le";
- for (size_t nsamps = 1; nsamps < 16; nsamps++) {
- test_convert_types_f32(nsamps, id);
- }
-
- // try various lengths to test edge cases
- id.output_format = "f32_item32_be";
for (size_t nsamps = 1; nsamps < 16; nsamps++) {
- test_convert_types_f32(nsamps, id);
+ test_convert_types_fc32(nsamps, id);
}
}