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
|