Thanks Marcus. I've done a little more debugging with a simple C++ example and the B210, and discovered that calling get_rx_stream() seems to be slightly changing the device time by random amounts when 2 channels are used. It doesn't matter if I actually issue a stream command or collect any data. Maybe that will be helpful in reproducing/isolating the issue without the three B210 setup I was using before.
-Michael On Tue, Feb 25, 2020 at 1:35 PM Marcus D. Leech via USRP-users < usrp-users@lists.ettus.com> wrote: > On 02/25/2020 09:33 AM, Michael Wentz via USRP-users wrote: > > Marcus, > > Thanks for the suggestion. I added set_subdev_spec('A:A A:B') for the 2 > channel case, but the results are the same. > > I also tried this code with a pair of X310s receiving. The results are > somewhat similar: > - Channel 0 (A:0) across 2 devices: no delay > - Channel 1 (B:0) across 2 devices: no delay > - Channels 0 and 1 (A:0 B:0) across 2 devices: channel 0 is in sync > between the two, but channel 1 is not - and channel 1 is not in sync with > channel 0 on either device. > - Unlike with the B210s, the X310s both report the same time after > streaming. > > Let us focus on the B210 first. > > I've been through your code, and I cannot find anything wrong with it. > Now, I recall that there was a bug in earlier UHD with the two > clocks in the B210 (each conceptual radio block in the FPGA includes its > own time-of-day clock), and they wouldn't always get reset > together, even with set_time_next_pps(), so there would be some skew. > > This, or something related may be the root cause of your issue. The > engineer who most recently worked on B210 is being consulted > on this, and hopefully myself or Sam Reiter will have an answer soon. > > > > I've done this in the past with many X310s (each with 2 channels) with > success, but that was with an older/pre-RFNoC UHD circa 2016. I'm using > pretty much the same code here. Anything else I can try to time sync > multiple devices? > > -Michael > > On Mon, Feb 24, 2020 at 9:07 PM Marcus D. Leech via USRP-users < > usrp-users@lists.ettus.com> wrote: > >> On 02/24/2020 05:10 PM, Michael Wentz via USRP-users wrote: >> >> Hi, >> >> I'm trying to synchronize multiple B210s to receive data at the same >> time. I'm only concerned about sample time alignment across devices, and am >> aware there is a phase offset that will need to be calibrated out >> separately. I've had success with 1 RX channel across 2 B210s, but >> something seems wrong when using 2 RX channels across 2 B210s. >> >> I've pasted a program below to help reproduce this issue. It uses 1 B210 >> to TX noise and 2 separate B210s to RX at the same time. There is a 40 dB >> attenuator on the output of the transmitting B210, then a 4 way splitter, >> and equal length cables to both receivers on the other 2 B210s. >> Additionally, the 2 receiving B210s get 10 MHz and PPS from a common >> OctoClock-G. >> >> Here are 10 observations of sample offsets between devices (1 MSPS rate): >> Only channel 0 on both RX: [0,0,0,0,0,0,0,0,0,0] >> Only channel 1 on both RX: [0,0,0,0,0,0,0,0,0,0] >> Channels 0 and 1 simultaneously on both >> RX: [14,1,-126,48,2,-88,17,-204,-96,2] >> >> In the 2 channel case both channels on one device are always aligned, but >> are offset from the channels on the other device. Both of the receiving >> B210s show the same time last PPS when asked before the streams start. The >> times in the RX metadata also match. But after the streams stop, the >> devices respond with different times - neither of which are correct - and >> the difference between them matches what I'm measuring. >> >> I'm using UHD 3.14.1.1 and GNU Radio 3.8 on RHEL 7.7. Any ideas? >> >> Thanks, >> Michael >> >> ---------- >> >> #!/usr/bin/env python3 >> >> from gnuradio import gr, uhd, analog, blocks >> import time >> import numpy as np >> >> tx_serial = '30D1***' >> rx_serials = ['3153***', '3153***'] >> rx_channels = [0, 1] >> >> n_rx = len(rx_serials) >> n_chan = len(rx_channels) >> >> class top_block(gr.top_block): >> def __init__(self): >> gr.top_block.__init__(self) >> >> # transmit path >> noise_src = analog.noise_source_c(analog.GR_GAUSSIAN, 0.1) >> tx_strm_args = uhd.stream_args(cpu_format='fc32', channels=[0]) >> tx = uhd.usrp_sink('serial=' + tx_serial, tx_strm_args) >> tx.set_clock_rate(16e6) >> tx.set_samp_rate(1e6) >> tx.set_center_freq(1e9) >> tx.set_gain(40) >> tx.set_antenna('TX/RX') >> self.connect(noise_src, tx) >> >> # receive path >> rx_strm_args = uhd.stream_args(cpu_format='fc32', >> channels=rx_channels) >> self.rx = [None]*n_rx >> head = [None]*n_rx*n_chan >> file_sink = [None]*n_rx*n_chan >> for i in range(n_rx): >> self.rx[i] = uhd.usrp_source('serial=' + rx_serials[i], >> rx_strm_args) >> self.rx[i].set_clock_rate(16e6) >> self.rx[i].set_samp_rate(1e6) >> self.rx[i].set_center_freq(1e9) >> self.rx[i].set_gain(40) >> self.rx[i].set_antenna('RX2') >> self.rx[i].set_time_source('external') >> self.rx[i].set_clock_source('external') >> for j in range(n_chan): >> ch = i*n_chan + j >> head[ch] = blocks.head(2*gr.sizeof_float, 10000) >> f = 'rx_%d%d.dat' % (i, j) >> file_sink[ch] = blocks.file_sink(2*gr.sizeof_float, f) >> file_sink[ch].set_unbuffered(True) >> self.connect((self.rx[i], j), head[ch], file_sink[ch]) >> >> # make sure 10 MHz ref is locked >> for i in range(n_rx): >> while not >> self.rx[i].get_mboard_sensor('ref_locked').to_bool(): >> print('RX %d waiting for 10 MHz ref lock...' % i) >> time.sleep(1) >> print('RX %d 10 MHz ref locked' % i) >> >> # time sync the two receivers >> for i in range(n_rx): >> t = self.rx[i].get_time_last_pps().get_real_secs() >> print('RX %d time before align: %r' % (i, t)) >> time_last_pps = self.rx[0].get_time_last_pps().get_real_secs() >> while time_last_pps == >> self.rx[0].get_time_last_pps().get_real_secs(): >> time.sleep(0.1) >> for i in range(n_rx): >> self.rx[i].set_time_next_pps(uhd.time_spec_t(0)) >> time_last_pps = self.rx[0].get_time_last_pps().get_real_secs() >> while time_last_pps == >> self.rx[0].get_time_last_pps().get_real_secs(): >> time.sleep(0.1) >> for i in range(n_rx): >> t = self.rx[i].get_time_last_pps().get_real_secs() >> print('RX %d time after align: %r' % (i, t)) >> >> # collect in the future >> rx_time = uhd.time_spec_t(2) >> for i in range(n_rx): >> self.rx[i].set_start_time(rx_time) >> self.rx[i].set_recv_timeout(3) >> >> # run flowgraph >> tb = top_block() >> tb.start() >> time.sleep(5) >> tb.stop() >> >> # print time >> t = tb.rx[0].get_time_last_pps().get_real_secs() >> while t == tb.rx[0].get_time_last_pps().get_real_secs(): >> time.sleep(0.1) >> for i in range(n_rx): >> t = tb.rx[i].get_time_last_pps().get_real_secs() >> print('RX %d time after run: %r' % (i, t)) >> >> # cross-correlate receivers and print delay >> y = [None]*n_rx*n_chan >> for i in range(n_rx): >> for j in range(n_chan): >> ch = i*n_chan + j >> y[ch] = np.fromfile('rx_%d%d.dat' % (i, j), dtype=np.complex64) >> r = np.correlate(y[0], y[ch], mode='full') >> max_lag = np.max([len(y[0]), len(y[ch])]) - 1 >> lags = np.arange(-max_lag, max_lag+1) >> d = lags[np.argmax(np.abs(r))] >> print('RX %d, channel %d: %d samples' % (i, j, d)) >> >> >> _______________________________________________ >> USRP-users mailing >> listUSRP-users@lists.ettus.comhttp://lists.ettus.com/mailman/listinfo/usrp-users_lists.ettus.com >> >> IN the two channel case, you should probably set the subdev spec to: >> >> A:A A:B >> >> I didn't set a set_rx_subdev_spec() in the code. >> >> >> _______________________________________________ >> USRP-users mailing list >> USRP-users@lists.ettus.com >> http://lists.ettus.com/mailman/listinfo/usrp-users_lists.ettus.com >> > > > _______________________________________________ > USRP-users mailing > listUSRP-users@lists.ettus.comhttp://lists.ettus.com/mailman/listinfo/usrp-users_lists.ettus.com > > > _______________________________________________ > USRP-users mailing list > USRP-users@lists.ettus.com > http://lists.ettus.com/mailman/listinfo/usrp-users_lists.ettus.com >
_______________________________________________ USRP-users mailing list USRP-users@lists.ettus.com http://lists.ettus.com/mailman/listinfo/usrp-users_lists.ettus.com