uhd icon indicating copy to clipboard operation
uhd copied to clipboard

UHD transmit does not saturate IQ levels for all architectures.

Open drmpeg opened this issue 2 years ago • 1 comments

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

drmpeg avatar Oct 11 '21 22:10 drmpeg

Note: The generic converter will also fail on x86.

mbr0wn avatar Dec 21 '21 11:12 mbr0wn

The NEON fixes were merged a while back, we just forgot to close this.

mbr0wn avatar Mar 20 '24 14:03 mbr0wn