Package: uhd / 3.15.0.0-4

0036-Add-TwinRX-support-to-phase-alignment-script.patch Patch series | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
From b23f9ef4948d31a55714bcff0637eec05bc0ea6a Mon Sep 17 00:00:00 2001
From: erickshepherdNI <erickshepherd@ni.com>
Date: Wed, 12 Feb 2020 13:26:50 -0600
Subject: [PATCH 36/62] Add TwinRX support to phase alignment script

---
 tools/gr-usrptest/apps/uhd_phase_alignment.py | 76 ++++++++++++++-----
 1 file changed, 57 insertions(+), 19 deletions(-)

diff --git a/tools/gr-usrptest/apps/uhd_phase_alignment.py b/tools/gr-usrptest/apps/uhd_phase_alignment.py
index 532aec2ed..edf12a231 100755
--- a/tools/gr-usrptest/apps/uhd_phase_alignment.py
+++ b/tools/gr-usrptest/apps/uhd_phase_alignment.py
@@ -114,6 +114,8 @@ def parse_args():
                        help="Set LO export {True, False} for each channel with a comma-separated list.")
     parser.add_argument("--lo-source",
                        help="Set LO source {None, internal, companion, external} for each channel with a comma-separated list. None skips this channel.")
+    parser.add_argument("--twinrx", type=bool, default=False,
+                        help="Set if the device is a TwinRX")
     # Signal Source
     parser.add_argument("--source-plugin", type=str, default="default",
                         help="Select source plugin. This can either be one of"
@@ -244,19 +246,6 @@ def setup_usrp(args):
         return None
     # At this point, we can assume our device has valid and locked clock and PPS
 
-    # Set the LO source and export
-    if (args.lo_export is not None) and (args.lo_source is not None):
-        (args.lo_source, args.lo_export) = normalize_lo_source_export_sel(args)
-        for chan, lo_source, lo_export in zip(args.channels, args.lo_source, args.lo_export):
-            if lo_export == "True":
-                logger.info("LO export enabled on channel %s", chan)
-                usrp.set_rx_lo_export_enabled(True, "all", chan)
-                usrp.set_tx_lo_export_enabled(True, "all", chan)
-            if lo_source != "None":
-                logger.info("Channel %s source set to %s", chan, lo_source)
-                usrp.set_rx_lo_source(lo_source, "all", chan)
-                usrp.set_tx_lo_source(lo_source, "all", chan)
-
     # Determine channel settings
     # TODO: Add support for >2 channels! (TwinRX)
     if len(args.channels) != 2:
@@ -268,6 +257,21 @@ def setup_usrp(args):
         usrp.set_rx_rate(args.rate, chan)
         usrp.set_rx_gain(args.gain, chan)
 
+    # Set the LO source and export
+    if (args.lo_export is not None) and (args.lo_source is not None):
+        (args.lo_source, args.lo_export) = normalize_lo_source_export_sel(args)
+        for chan, lo_source, lo_export in zip(args.channels, args.lo_source, args.lo_export):
+            if lo_export == "True":
+                logger.info("LO export enabled on channel %s", chan)
+                usrp.set_rx_lo_export_enabled(True, "all", chan)
+                if args.twinrx is False:
+                    usrp.set_tx_lo_export_enabled(True, "all", chan)
+            if lo_source != "None":
+                logger.info("Channel %s source set to %s", chan, lo_source)
+                usrp.set_rx_lo_source(lo_source, "all", chan)
+                if args.twinrx is False:
+                    usrp.set_tx_lo_source(lo_source, "all", chan)
+
     # Actually synchronize devices
     # We already know we have >=2 channels, so don't worry about that
     if args.sync in ['default', "pps"]:
@@ -317,14 +321,37 @@ def generate_time_spec(usrp, time_delta=0.05):
     return usrp.get_time_now() + uhd.types.TimeSpec(time_delta)
 
 
-def tune_usrp(usrp, freq, channels, delay=CMD_DELAY):
-    """Synchronously set the device's frequency"""
+def tune_usrp(usrp, freq, channels, lo_source, delay=CMD_DELAY):
+    """Synchronously set the device's frequency.
+       If a channel is using an internal LO it will be tuned first
+       and every other channel will be manually tuned based on the response.
+       This is to account for the internal LO channel having an offset in the actual DSP frequency.
+       Then all channels are synchronously tuned."""
+
+    treq = uhd.types.TuneRequest(freq)
+    lo_source_channel = -1
+    for chan, lo in zip(channels, lo_source):
+        if lo == "internal":
+            lo_source_channel = chan
+    if lo_source_channel != -1:
+        treq.dsp_freq = (usrp.set_rx_freq(uhd.types.TuneRequest(freq), lo_source_channel)).actual_dsp_freq
+        treq.target_freq = freq
+        treq.rf_freq = freq
+        treq.rf_freq_policy = uhd.types.TuneRequestPolicy(ord('M'))
+        treq.dsp_freq_policy = uhd.types.TuneRequestPolicy(ord('M'))
+        for chan, lo_source in zip(channels, lo_source):
+            if lo_source == "internal":
+                continue
+            usrp.set_rx_freq(treq, chan)
     usrp.set_command_time(generate_time_spec(usrp, time_delta=delay))
     for chan in channels:
-        usrp.set_rx_freq(uhd.types.TuneRequest(freq), chan)
+        usrp.set_rx_freq(treq, chan)
+    usrp.clear_command_time()
+    time.sleep(delay)
+
 
 
-def recv_aligned_num_samps(usrp, streamer, num_samps, freq, channels=(0,)):
+def recv_aligned_num_samps(usrp, streamer, num_samps, freq, lo_source, channels=(0,)):
     """
     RX a finite number of samples from the USRP
     :param usrp: MultiUSRP object
@@ -338,7 +365,7 @@ def recv_aligned_num_samps(usrp, streamer, num_samps, freq, channels=(0,)):
     result = np.empty((len(channels), num_samps), dtype=np.complex64)
 
     # Tune to the desired frequency
-    tune_usrp(usrp, freq, channels)
+    tune_usrp(usrp, freq, channels, lo_source)
 
     metadata = uhd.types.RXMetadata()
     buffer_samps = streamer.get_max_num_samps() * 10
@@ -444,6 +471,7 @@ def check_results(alignment_stats, drift_thresh, stddev_thresh):
     success = True  # Whether or not we've exceeded a threshold
     msg = ""
     for freq, stats_list in sorted(alignment_stats.items()):
+        band_success = True
         # Try to grab the test frequency for the frequency band
         try:
             test_freq = stats_list[0].get("test_freq")
@@ -462,6 +490,7 @@ def check_results(alignment_stats, drift_thresh, stddev_thresh):
             mean_deg = run_dict.get("mean", 0.) * 180 / np.pi
             stddev_deg = run_dict.get("stddev", 0.) * 180 / np.pi
             if stddev_deg > stddev_thresh:
+                band_success = False
                 success = False
 
             msg += "{:.2f}MHz<-{:.2f}MHz: {:.3f} deg +- {:.3f}\n".format(
@@ -472,8 +501,11 @@ def check_results(alignment_stats, drift_thresh, stddev_thresh):
         # Report the largest difference in mean values of runs
         max_drift = calc_max_drift(mean_list)
         if max_drift > drift_thresh:
+            band_success = False
             success = False
         msg += "--Maximum drift over runs: {:.2f} degrees\n".format(max_drift)
+        if band_success is False:
+            msg += "Failure!\n"
         # Print a newline to separate frequency bands
         msg += "\n"
 
@@ -536,6 +568,7 @@ def main():
         if args.easy_tune:
             # Round to the nearest MHz
             tune_freq = np.round(tune_freq, -6)
+
         # Request the SigGen tune to our test frequency plus some offset away
         # the device's LO
         src_gen.tune(tune_freq + args.tone_offset, current_power)
@@ -548,7 +581,7 @@ def main():
             for i in range(NUM_RETRIES):
                 # Tune to a random frequency in each of the frequency bands...
                 tune_away_freq = npr.uniform(tune_away_start, tune_away_stop)
-                tune_usrp(usrp, tune_away_freq, args.channels)
+                tune_usrp(usrp, tune_away_freq, args.channels, args.lo_source)
                 time.sleep(args.skip_time)
 
                 logger.info("Receiving samples, take %d, (%.2fMHz -> %.2fMHz)",
@@ -559,6 +592,7 @@ def main():
                                                streamer,
                                                nsamps,
                                                tune_freq,
+                                               args.lo_source,
                                                args.channels)
                 if samps.size >= nsamps:
                     break
@@ -602,6 +636,10 @@ def main():
                      tune_freq/1e6,
                      calc_max_drift(run_means) * 180 / np.pi,
                      max(run_stddevs) * 180. / np.pi)
+        if args.drift_threshold < calc_max_drift(run_means) * 180 / np.pi:
+            logger.info("Drift threshold of %.1f has been exceeded by %.1f degrees", args.drift_threshold, (calc_max_drift(run_means) * 180 / np.pi) - args.drift_threshold)
+        if args.stddev_threshold < max(run_stddevs) * 180. / np.pi:
+            logger.info("Max stddev threshold of %.2f has been exceeded by %.2f degrees", args.stddev_threshold, (max(run_stddevs) * 180. / np.pi) - args.stddev_threshold)
         all_alignment_stats[freq_start] = alignment_stats
         # Increment the power level for the next run
         current_power += args.power_step
-- 
2.20.1