convert: Add CHDR converters

The difference between the _chdr converters and the _item32_ converters
is that the former do not require item32 boundaries, they do not require
endianness swapping, and they don't use IQ swapping either.

This is possible because the FPGA will do byte-swapping.
This commit is contained in:
Martin Braun 2019-07-18 14:36:14 -07:00
parent 93d8c6f83b
commit fed32af080
2 changed files with 174 additions and 32 deletions

View file

@ -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'):

View file

@ -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);
}
}