uhd
uhd copied to clipboard
UHD transmit does not saturate IQ levels for all architectures.
Issue Description
For non-x86 architectures, the transmit conversion routines do not saturate the output levels to +/- 1.0. This causes heavy distortion of the output RF signal even with minor excursions beyond +/- 1.0.
Setup Details
The test setup for armv7 (NEON) is a Beagleboard X15 and the test setup for RISC-V (generic) is a HiFive Unmatched. Both systems are using an Ettus B210.
Expected Behavior
When values above/below +/- 1.0 are sent to the UHD sink, the conversions routines should saturate the values to +/- 1.0.
Actual Behavior
When values above/below +/- 1.0 are sent to the UHD sink, the conversions routines for x86 saturate properly, but the conversion routines for NEON and generic do not.
Steps to reproduce the problem
Send values above/below +/- 1.0 and either instrument the conversion routines or observe the RF signal on a spectrum analyzer.
Additional Information
The fix for NEON is easy, and I'll be submitting a pull request soon (https://github.com/EttusResearch/uhd/pull/503). The fix for generic is also simple, but due to the complexity of UHD, I'm not exactly sure how to implement it correctly. This is the current patch I'm using for RISC-V (generic):
diff --git a/host/lib/convert/convert_common.hpp b/host/lib/convert/convert_common.hpp
index 6ee9a651a..7b1b7f741 100644
--- a/host/lib/convert/convert_common.hpp
+++ b/host/lib/convert/convert_common.hpp
@@ -103,8 +103,22 @@ template <typename T>
UHD_INLINE item32_t xx_to_item32_sc16_x1(
const std::complex<T>& num, const double scale_factor)
{
- uint16_t real = int16_t(num.real() * float(scale_factor));
- uint16_t imag = int16_t(num.imag() * float(scale_factor));
+ int32_t tmp_real = int32_t(num.real() * float(scale_factor));
+ int32_t tmp_imag = int32_t(num.imag() * float(scale_factor));
+ if (tmp_real < -32768) {
+ tmp_real = -32768;
+ }
+ else if (tmp_real > 32767) {
+ tmp_real = 32767;
+ }
+ if (tmp_imag < -32768) {
+ tmp_imag = -32768;
+ }
+ else if (tmp_imag > 32767) {
+ tmp_imag = 32767;
+ }
+ uint16_t real = int16_t(tmp_real);
+ uint16_t imag = int16_t(tmp_imag);
return (item32_t(real) << 16) | (item32_t(imag) << 0);
}
Note: The generic converter will also fail on x86.
The NEON fixes were merged a while back, we just forgot to close this.