def find_corr_idx(waveRxA, waveRxB): """ Find correlation indexes Input: waveRxA - vector of IQ samples from RF chain A waveRxB - vector of IQ samples from RF chain B Output: idx_mat - matrix with correlation indexes at all boards """ idx_mat = np.empty( (waveRxA.shape[0], waveRxA.shape[1])) # Matrix: 2x#radios. two pilots idx_mat[:] = np.nan lts_thresh = 0.8 # Iterate over the multiple entries for i in range(waveRxA.shape[0]): for j in range(waveRxA.shape[1]): # Both channels best_peakA, all_peaksA, corrA = find_lts(waveRxA[i, j], thresh=lts_thresh, flip=False) best_peakB, all_peaksB, corrB = find_lts(waveRxB[i, j], thresh=lts_thresh, flip=False) # print("Best peak BOARD: {}, A: {} and B: {}".format(j, best_peakA, best_peakB)) # plt.figure(100) # plt.plot(corrA) # plt.plot(corrB) # plt.show() # Check if LTS found if not best_peakA: # and not best_peakB: print("No LTS Found!") continue if not (best_peakA == best_peakB): print("Same board, different indexes. Wut??") idx_mat[i, j] = best_peakA # best_peakA if best_peakA else best_peakB return idx_mat
def pilot_finder(samples, pilot_type, flip=False, pilot_seq=[]): """ Find pilots from clients to each of the base station antennas Input: samples - Raw samples from pilots and data. Dimensions: vector [1 x num samples] pilot_type - Type of TX pilot (e.g., 802.11 LTS) flip - Needed for finding LTS function Output: pilot - Received pilot (from multiple clients) tx_pilot - Transmitted pilot (same pilot sent by all clients) """ if pilot_type == 'lts-half' or pilot_type == 'lts-full': lts_thresh = 0.8 best_pk, lts_pks, lts_corr = find_lts(samples, thresh=lts_thresh, flip=flip, lts_seq=pilot_seq) # full lts contains 2.5 64-sample-LTS sequences, we need only one symbol lts, lts_f = generate_training_seq(preamble_type='lts', cp=32, upsample=1) if not (pilot_seq.size == 0): # pilot provided, overwrite the one returned above lts = pilot_seq lts_syms_len = len(lts) pilot_thresh = lts_thresh * np.max(lts_corr) # We'll need the transmitted version of the pilot (for channel estimation, for example) tx_pilot = [lts, lts_f] lts_start = 0 # Check if LTS found if not best_pk: print("SISO_OFDM: No LTS Found! Continue...") pilot = np.array([]) return pilot, tx_pilot, lts_corr, pilot_thresh, best_pk, lts_start # If beginning of frame was not captured in current buffer if (best_pk - lts_syms_len) < 0: print("TOO EARLY. Continue... ") pilot = np.array([]) return pilot, tx_pilot, lts_corr, pilot_thresh, best_pk, lts_start if best_pk > len(samples): print("TOO LATE. Continue... ") pilot = np.array([]) return pilot, tx_pilot, lts_corr, pilot_thresh, best_pk, lts_start # Get pilot lts_start = best_pk - lts_syms_len + 0 # where LTS-CP start pilot = samples[lts_start:best_pk+0] else: raise Exception("Only LTS Pilots supported at the moment") return pilot, tx_pilot, lts_corr, pilot_thresh, best_pk, lts_start
def plot_data(pilot, rxdata, channelnum, framelen=512, framenum=100, pilotsymnum=1, uplinksymnum=0): # dc removal sampsRx = pilot[0].flatten() # find LTS assumes signal was generated according to this: # generate_training_seq(preamble_type='lts', cp=32, upsample=1) # a, b, peaks = lts.findLTS(sampsRx) a, b, peaks = find_lts(sampsRx, thresh=0.8, us=1, cp=32) fig, axes = plt.subplots(nrows=pilotsymnum + uplinksymnum + 2 + 1, ncols=1) sig_power = np.empty([pilotsymnum, framenum], dtype=float) sig_phase = np.empty([pilotsymnum, framenum], dtype=float) offset = 82 + 128 sc = 2 # subcarrier for i, ax in enumerate(axes): if i < pilotsymnum: label = "pilot %d " % i p = pilot[i].flatten() ax.plot(np.real(p), label=label + "I") ax.plot(np.imag(p), label=label + "Q") ax.legend() for k in range(framenum): signal = pilot[i, k, offset:offset + 64] sig_power[i, k] = np.mean(signal * np.conj(signal)) sig_phase[i, k] = np.angle(np.fft.fftshift( np.fft.fft(signal)))[sc] elif uplinksymnum > 0 and i < pilotsymnum + uplinksymnum: j = i - pilotsymnum d = rxdata[j].flatten() label = "rx data %d " % j ax.plot(np.real(d), label=label + "I") ax.plot(np.imag(d), label=label + "Q") ax.legend() elif i == pilotsymnum + uplinksymnum: for m in range(pilotsymnum): ax.plot(sig_power[m], label='pilot %d power' % m) ax.legend() elif i == pilotsymnum + uplinksymnum + 1: for m in range(pilotsymnum): ax.plot(sig_phase[m], label='pilot %d phase sc %d' % (m, sc)) ax.legend() else: ax.plot(np.real(peaks), label='Corr Peaks') plt.show()
def init(hub, bnodes, ref_ant, ampl, rate, freq, txgain, rxgain, cyc_prefix, num_samps, prefix_length, postfix_length, take_duration, both_channels, plotter): """main function initializing all radios and performing calibration""" if hub != "": hub_dev = SoapySDR.Device(dict(driver="remote", serial=hub)) bsdrs = [ SoapySDR.Device(dict(driver="iris", serial=serial)) for serial in bnodes ] ant = 2 if both_channels else 1 num_sdrs = len(bsdrs) num_ants = num_sdrs * ant # assume trig_sdr is part of the master nodes trig_dev = None if hub != "": trig_dev = hub_dev else: trig_dev = bsdrs[0] #set params on both channels for sdr in bsdrs: info = sdr.getHardwareInfo() print("%s settings on device" % (info["frontend"])) for ch in [0, 1]: sdr.setBandwidth(SOAPY_SDR_TX, ch, 2.5 * rate) sdr.setBandwidth(SOAPY_SDR_RX, ch, 2.5 * rate) sdr.setSampleRate(SOAPY_SDR_TX, ch, rate) sdr.setSampleRate(SOAPY_SDR_RX, ch, rate) sdr.setFrequency(SOAPY_SDR_TX, ch, 'RF', freq - .75 * rate) sdr.setFrequency(SOAPY_SDR_RX, ch, 'RF', freq - .75 * rate) sdr.setFrequency(SOAPY_SDR_TX, ch, 'BB', .75 * rate) sdr.setFrequency(SOAPY_SDR_RX, ch, 'BB', .75 * rate) sdr.setGain(SOAPY_SDR_TX, ch, txgain) sdr.setGain(SOAPY_SDR_RX, ch, rxgain) sdr.setAntenna(SOAPY_SDR_RX, ch, "TRX") sdr.setDCOffsetMode(SOAPY_SDR_RX, ch, True) # Read initial gain settings read_lna = sdr.getGain(SOAPY_SDR_RX, 0, 'LNA') read_tia = sdr.getGain(SOAPY_SDR_RX, 0, 'TIA') read_pga = sdr.getGain(SOAPY_SDR_RX, 0, 'PGA') print("INITIAL GAIN - LNA: {}, \t TIA:{}, \t PGA:{}".format( read_lna, read_tia, read_pga)) #for ch in [0, 1]: # if calibrate: # sdr.writeSetting(SOAPY_SDR_RX, ch, "CALIBRATE", 'SKLK') # sdr.writeSetting(SOAPY_SDR_TX, ch, "CALIBRATE", '') sdr.writeSetting("RESET_DATA_LOGIC", "") if not both_channels: sdr.writeSetting(SOAPY_SDR_RX, 1, 'ENABLE_CHANNEL', 'false') sdr.writeSetting(SOAPY_SDR_TX, 1, 'ENABLE_CHANNEL', 'false') trig_dev.writeSetting("SYNC_DELAYS", "") sym_samps = num_samps + prefix_length + postfix_length print("num_samps = %d" % sym_samps) fft_size = 64 cp_len = 32 if cyc_prefix else 0 lts_sym, _ = generate_training_seq(preamble_type='lts', cp=cp_len, upsample=1) ofdm_len = len(lts_sym) zeros = np.array([0] * (num_samps - ofdm_len)) pilot = np.concatenate((lts_sym, zeros)).astype(np.complex64) wb_pilot = ampl * pilot wbz = np.array([0] * (sym_samps), np.complex64) pad1 = np.array([0] * (prefix_length), np.complex64) # to comprensate for front-end group delay pad2 = np.array([0] * (postfix_length), np.complex64) # to comprensate for rf path delay wb_pilot_pad = np.concatenate([pad1, wb_pilot, pad2]).astype(np.complex64) pilot_subcarriers = [7, 21, 43, 57] pilot_sc_num = len(pilot_subcarriers) # Create streams tx_streams = [ sdr.setupStream(SOAPY_SDR_TX, SOAPY_SDR_CF32, [0, 1]) for sdr in bsdrs ] rx_streams = [ sdr.setupStream(SOAPY_SDR_RX, SOAPY_SDR_CF32, [0, 1]) for sdr in bsdrs ] for r, sdr in enumerate(bsdrs): sdr.activateStream(tx_streams[r]) rx_samps = [[ np.empty(sym_samps).astype(np.complex64) for r in range(num_ants) ] for t in range(num_ants)] rx_f = [[np.empty(fft_size).astype(np.complex64) for r in range(num_ants)] for t in range(num_ants)] calib_mag = [ np.empty(fft_size).astype(np.complex64) for r in range(num_ants) ] calib_ang = [ np.empty(fft_size).astype(np.complex64) for r in range(num_ants) ] est_calib_mag = [ np.empty(fft_size).astype(np.complex64) for r in range(num_ants) ] est_calib_ang = [ np.empty(fft_size).astype(np.complex64) for r in range(num_ants) ] mag_buffer = [[ collections.deque(maxlen=NUM_BUFFER_SAMPS) for i in range(pilot_sc_num) ] for j in range(num_ants)] ang_buffer = [[ collections.deque(maxlen=NUM_BUFFER_SAMPS) for i in range(len(pilot_subcarriers)) ] for j in range(num_ants)] dummy = np.empty(sym_samps).astype(np.complex64) fig1, axes1 = plt.subplots(nrows=num_ants, ncols=2, figsize=(12, 8)) axes1[0, 0].set_title('Reciprocity Calibration Magnitude') axes1[0, 1].set_title('Reciprocity Calibration Phase') for m in range(num_ants): axes1[m, 0].set_xlim(0, NUM_BUFFER_SAMPS) axes1[m, 1].set_xlim(0, NUM_BUFFER_SAMPS) axes1[m, 0].set_ylim(0, 5) axes1[m, 1].set_ylim(-np.pi, np.pi) if m == ref_ant: axes1[m, 0].set_ylabel('Ant %d (ref)' % (m)) else: axes1[m, 0].set_ylabel('Ant %d' % (m)) lines10 = [[ axes1[m, 0].plot(range(NUM_BUFFER_SAMPS), np.zeros(NUM_BUFFER_SAMPS), label='SC %d' % (pilot_subcarriers[p]))[0] for p in range(pilot_sc_num) ] for m in range(num_ants)] lines11 = [[ axes1[m, 1].plot(range(NUM_BUFFER_SAMPS), np.zeros(NUM_BUFFER_SAMPS), label='SC %d' % (pilot_subcarriers[p]))[0] for p in range(pilot_sc_num) ] for m in range(num_ants)] for m in range(num_ants): for l in range(2): axes1[m, l].legend(fontsize=10) fig1.show() fig3, axes3 = plt.subplots(nrows=num_ants, ncols=2, figsize=(12, 8)) axes3[0, 0].set_title('Reciprocity Calibration Magnitude') axes3[0, 1].set_title('Reciprocity Calibration Phase') for m in range(num_ants): axes3[m, 0].set_xlim(-8, 72) axes3[m, 1].set_xlim(-8, 72) axes3[m, 0].set_ylim(0, 5) axes3[m, 1].set_ylim(-np.pi, np.pi) if m == ref_ant: axes3[m, 0].set_ylabel('Ant %d (ref)' % (m)) else: axes3[m, 0].set_ylabel('Ant %d' % (m)) lines300 = [ axes3[m, 0].plot(range(64), np.zeros(64), label='Measured Mag')[0] for m in range(num_ants) ] lines301 = [ axes3[m, 0].plot(range(64), np.zeros(64), label='Estimated Mag')[0] for m in range(num_ants) ] lines310 = [ axes3[m, 1].plot(range(64), np.zeros(64), label='Measured Ang')[0] for m in range(num_ants) ] lines311 = [ axes3[m, 1].plot(range(64), np.zeros(64), label='Estimated Ang')[0] for m in range(num_ants) ] for m in range(num_ants): for l in range(2): axes3[m, l].legend(fontsize=10) fig3.show() if plotter: fig2, axes2 = plt.subplots(nrows=num_ants, ncols=num_ants, figsize=(12, 12)) for m in range(num_ants): for l in range(num_ants): axes2[m, l].set_xlim(0, sym_samps) axes2[m, l].set_ylim(-1, 1) axes2[m, l].set_ylabel('Tx Ant %d, Rx Ant %d' % (m, l)) axes2[m, l].legend(fontsize=10) lines20 = [[ axes2[m, l].plot(range(sym_samps), np.real(wbz), label='Pilot TxAnt %d RxAnt %d (real)' % (m, l))[0] for l in range(num_ants) ] for m in range(num_ants)] lines21 = [[ axes2[m, l].plot(range(sym_samps), np.imag(wbz), label='Pilot TxAnt %d RxAnt %d (imag)' % (m, l))[0] for l in range(num_ants) ] for m in range(num_ants)] lines22 = [[ axes2[m, l].plot(range(sym_samps), sym_samps * [LTS_THRESH])[0] for m in range(num_ants) ] for l in range(num_ants)] fig2.show() first = True take = 0 prev_take = take cur_mean_ang = np.ndarray(shape=(num_ants, pilot_sc_num), dtype=float) cur_mean_mag = np.ndarray(shape=(num_ants, pilot_sc_num), dtype=float) prev_mean_ang = np.ndarray(shape=(num_ants, pilot_sc_num), dtype=float) prev_mean_mag = np.ndarray(shape=(num_ants, pilot_sc_num), dtype=float) first_mean_ang = np.ndarray(shape=(num_ants, pilot_sc_num), dtype=float) first_mean_mag = np.ndarray(shape=(num_ants, pilot_sc_num), dtype=float) signal.signal(signal.SIGINT, signal_handler) begin = time.time() prev_time = begin while RUNNING: for d in range(num_sdrs): ref_sdr = bsdrs[d] flags = SOAPY_SDR_WAIT_TRIGGER | SOAPY_SDR_END_BURST # transmit pilot from node m sr = ref_sdr.writeStream(tx_streams[d], [wb_pilot_pad, wbz], sym_samps, flags) if sr.ret == -1: print("bad write") flags = SOAPY_SDR_WAIT_TRIGGER | SOAPY_SDR_END_BURST for r, sdr in enumerate(bsdrs): if r != d: sdr.activateStream(rx_streams[r], flags, 0, sym_samps) trig_dev.writeSetting("TRIGGER_GEN", "") for r, sdr in enumerate(bsdrs): if r != d: sr = sdr.readStream(rx_streams[r], [rx_samps[d][r], dummy], sym_samps, timeoutUs=int(1e6)) if sr.ret != sym_samps: print("bad read %d" % sr.ret) bad_data = False for m in range(num_ants): if bad_data: break for p in range(num_ants): if m != p: rx_samps[m][p] -= np.mean(rx_samps[m][p]) best_peak, _, _ = find_lts(rx_samps[m][p], thresh=LTS_THRESH, flip=True) offset = 0 if not best_peak else best_peak - len( lts_sym) + cp_len if offset < 150: print("bad data, skip") bad_data = True #break rx_m_p = rx_samps[m][p] rx_f1 = np.fft.fft(rx_m_p[offset:offset + fft_size], fft_size, 0) rx_f2 = np.fft.fft( rx_m_p[offset + fft_size:offset + 2 * fft_size], fft_size, 0) rx_f[m][p] = (rx_f1 + rx_f2) / 2 if plotter: lines20[m][p].set_ydata(np.real(rx_samps[m][p])) lines21[m][p].set_ydata(np.imag(rx_samps[m][p])) lines22[m][p].set_data(offset, np.linspace(-1.0, 1.0, num=100)) if bad_data: continue mag_model = LinearRegression() ang_model = LinearRegression() for m in range(num_ants): if m == ref_ant: calib_mag[m] = np.ones(fft_size) calib_ang[m] = np.zeros(fft_size) else: calib_mag[m] = np.divide(np.abs(rx_f[m][ref_ant]), np.abs(rx_f[ref_ant][m])) calib_ang[m] = np.angle(rx_f[m][ref_ant] * np.conj(rx_f[ref_ant][m])) for c in range(pilot_sc_num): s = pilot_subcarriers[c] mag_buffer[m][c].append(calib_mag[m][s]) ang_buffer[m][c].append(calib_ang[m][s]) x = np.asarray(pilot_subcarriers).reshape((-1, 1)) y_mag = calib_mag[m][pilot_subcarriers] y_ang = calib_ang[m][pilot_subcarriers] mag_model.fit(x, y_mag) ang_model.fit(x, y_ang) for c in range(64): est_calib_mag[m][ c] = mag_model.intercept_ + mag_model.coef_ * c est_calib_ang[m][ c] = ang_model.intercept_ + ang_model.coef_ * c for m in range(num_ants): for p in range(pilot_sc_num): lines10[m][p].set_data(range(len(mag_buffer[m][p])), mag_buffer[m][p]) lines11[m][p].set_data(range(len(mag_buffer[m][p])), ang_buffer[m][p]) lines300[m].set_ydata(calib_mag[m]) lines301[m].set_ydata(est_calib_mag[m]) lines310[m].set_ydata(calib_ang[m]) lines311[m].set_ydata(est_calib_ang[m]) fig1.canvas.draw() fig1.show() fig3.canvas.draw() fig3.show() if plotter: fig2.canvas.draw() fig2.show() cur_time = time.time() if (cur_time - prev_time > take_duration): take_num = take - prev_take for m in range(num_ants): for p in range(pilot_sc_num): mag_buffer_list = list(mag_buffer[m][p]) ang_buffer_list = list(ang_buffer[m][p]) mag_buffer_shot = mag_buffer_list[-take_num:] ang_buffer_shot = ang_buffer_list[-take_num:] cur_mean_mag[m, p] = np.mean(mag_buffer_shot) cur_mean_ang[m, p] = np.mean(ang_buffer_shot) if first: first_mean_mag = cur_mean_mag.copy() first_mean_ang = cur_mean_ang.copy() first = False mag_drift_last = cur_mean_mag - prev_mean_mag ang_drift_last = np.unwrap(cur_mean_ang - prev_mean_ang) mag_drift_start = cur_mean_mag - first_mean_mag ang_drift_start = np.unwrap(cur_mean_ang - first_mean_ang) print("%d total takes, %d new takes, %f secs elapsed:" % (take, take_num, cur_time - begin)) print("Mag drift from last take:") print(mag_drift_last[1:, :]) print("") print("Ang drift from last take:") print(ang_drift_last[1:, :]) print("") print("Mag drift from first take:") print(mag_drift_start[1:, :]) print("") print("Ang drift from first take:") print(ang_drift_start[1:, :]) print("") print("") print("") prev_time = cur_time prev_take = take prev_mean_mag = cur_mean_mag.copy() prev_mean_ang = cur_mean_ang.copy() take += 1 for r, sdr in enumerate(bsdrs): sdr.closeStream(tx_streams[r]) sdr.closeStream(rx_streams[r])
def animate(i, num_samps, recorder, agc_en, wait_trigger, info): global sdr, rxStream, freqScale, sampsRx, frameCounter, fft_size, Rate, num_samps_circ_buff, rssi_circ_buff, pwr_circ_buff # Trigger AGC if agc_en: if frameCounter == 10: print(" *** ENABLE AGC/PKT DETECT *** ") sdr.writeRegister("IRIS30", FPGA_IRIS030_WR_PKT_DET_ENABLE, 1) sdr.writeRegister("IRIS30", FPGA_IRIS030_WR_AGC_ENABLE_FLAG, 1) if frameCounter == 20: print(" *** DONE WITH PREVIOUS FRAME, ASSUME NEW FRAME INCOMING *** ") sdr.writeRegister("IRIS30", FPGA_IRIS030_WR_PKT_DET_NEW_FRAME, 1) sdr.writeRegister("IRIS30", FPGA_IRIS030_WR_PKT_DET_NEW_FRAME, 0) # DO NOT REMOVE ME!! Disable right away # Read samples into this buffer sampsRx = [np.zeros(num_samps, np.complex64), np.zeros(num_samps, np.complex64)] buff0 = sampsRx[0] # RF Chain 1 buff1 = sampsRx[1] # RF Chain 2 flags = SOAPY_SDR_END_BURST if wait_trigger: flags |= SOAPY_SDR_WAIT_TRIGGER sdr.activateStream(rxStream, flags, # flags 0, # timeNs (dont care unless using SOAPY_SDR_HAS_TIME) buff0.size) # numElems - this is the burst size sr = sdr.readStream(rxStream, [buff0, buff1], buff0.size) if sr.ret != buff0.size: print("Read RX burst of %d, requested %d" % (sr.ret, buff0.size)) # DC removal for i in [0, 1]: sampsRx[i] -= np.mean(sampsRx[i]) # Find LTS peaks (in case LTSs were sent) lts_thresh = 0.8 a, b, peaks0 = find_lts(sampsRx[0], thresh=lts_thresh) a, b, peaks1 = find_lts(sampsRx[1], thresh=lts_thresh) # If recording samples if recorder is not None: frame = np.empty((2, buff0.size), dtype='complex64') frame[0] = sampsRx[0] frame[1] = sampsRx[1] recorder.save_frame(sampsRx, sr.timeNs) # Store received samples in binary file (second method of storage) write_to_file('./data_out/rxsamps', sampsRx) # Magnitude of IQ Samples (RX RF chain A) I = np.real(sampsRx[0]) Q = np.imag(sampsRx[0]) IQmag = np.mean(np.sqrt(I**2 + Q**2)) # Retrieve RSSI measured from digital samples at the LMS7, and convert to PWR in dBm agc_avg = 0 rssi, PWRdBFS = getDigitalRSSI(sdr, agc_avg) # dBFS # Compute Power of Time Domain Signal sigRms = np.sqrt(np.mean(sampsRx[0] * np.conj(sampsRx[0]))) sigPwr = np.real(sigRms) ** 2 sigPwr_dB = 10 * np.log10(sigPwr) sigPwr_dBm = 10 * np.log10(sigPwr / 1e-3) # Compute Power of Frequency Domain Signal (FFT) f1, powerBins, noiseFloor, pks = fft_power(sampsRx[0], Rate, num_bins=fft_size, peak=1.0, scaling='spectrum', peak_thresh=20) fftPower = bandpower(sampsRx[0], Rate, 0, Rate / 2) if fftPower <= 0: fftPower = 1e-15 # Remove warning fftPower_dB = 10 * np.log10(fftPower) fftPower_dBm = 10 * np.log10(fftPower / 1e-3) # Retrieve RSSI computed in the FPGA rssi_fpga = int(sdr.readRegister("IRIS30", FPGA_IRIS030_RD_MEASURED_RSSI)) Vrms_fpga = (rssi_fpga / 2.0 ** 16) * (1 / np.sqrt(2.0)) # Vrms = Vpeak/sqrt(2) (In Volts) - 16 bit value PWRrms_fpga = (Vrms_fpga ** 2.0) / 50.0 # 50 Ohms load (PWRrms in Watts) PWRdBm_fpga = 10.0 * np.log10(PWRrms_fpga) + 30 # P(dBm)=10*log10(Prms/1mW) OR P(dBm)=10*log10(Prms)+30 # Circular buffer - continuously display data rssiPwrBuffer.append(PWRdBFS) timePwrBuffer.append(sigPwr_dB) freqPwrBuffer.append(fftPower_dB) noisPwrBuffer.append(noiseFloor) rssiPwrBuffer_fpga.append(PWRdBm_fpga) # Current gain values? lna_rd = sdr.getGain(SOAPY_SDR_RX, 0, 'LNA') # ChanA (0) tia_rd = sdr.getGain(SOAPY_SDR_RX, 0, 'TIA') # ChanA (0) pga_rd = sdr.getGain(SOAPY_SDR_RX, 0, 'PGA') # ChanA (0) if "CBRS" in info["frontend"]: lna1_rd = sdr.getGain(SOAPY_SDR_RX, 0, 'LNA1') # ChanA (0) lna2_rd = sdr.getGain(SOAPY_SDR_RX, 0, 'LNA2') # ChanA (0) attn_rd = sdr.getGain(SOAPY_SDR_RX, 0, 'ATTN') # ChanA (0) else: lna1_rd = [] lna2_rd = [] attn_rd = [] # Moving average (just for visualization purposes) circ_buff_idx = frameCounter % num_samps_circ_buff rssi_circ_buff[circ_buff_idx] = rssi pwr_circ_buff[circ_buff_idx] = PWRdBFS rssi_buff_avg = np.mean(rssi_circ_buff) pwr_buff_avg = np.mean(pwr_circ_buff) # Count number of frames received frameCounter = frameCounter + 1 print("RSSI: {} \t PWR: {} \t ||| GAINS - ATTN: {} \t LNA1: {} \t LNA2: {} \t LNA: {} \t TIA: {} \t PGA: {} ".format( rssi_buff_avg, pwr_buff_avg, attn_rd, lna1_rd, lna2_rd, lna_rd, tia_rd, pga_rd)) # Fill out data structures with measured data line1.set_data(range(buff0.size), np.real(sampsRx[0])) line2.set_data(range(buff0.size), np.imag(sampsRx[0])) line3.set_data(range(buff0.size), np.abs(sampsRx[0])) line4.set_data(range(buff0.size), np.abs(sampsRx[1])) line5.set_data(range(buff0.size), np.angle(sampsRx[0])) line6.set_data(range(buff0.size), np.angle(sampsRx[1])) line7.set_data(range(buff0.size), np.angle(sampsRx[0] * np.conj(sampsRx[1]))) line8.set_data(f1, powerBins) line9.set_data(range(len(rssiPwrBuffer)), rssiPwrBuffer) line10.set_data(range(len(timePwrBuffer)), timePwrBuffer) line11.set_data(range(len(freqPwrBuffer)), freqPwrBuffer) line12.set_data(range(len(noisPwrBuffer)), noisPwrBuffer) line13.set_data(range(len(rssiPwrBuffer_fpga)), rssiPwrBuffer_fpga) line14.set_data(range(buff0.size), np.real(peaks0[:buff0.size])) return line1, line2, line3, line4, line5, line6, line7, line8, line9, line10, line11, line12, line13, line14
def animate(i, num_samps_rd, rxStream, sdr, sdrTx, ofdm_params, tx_struct, ota, ofdm_obj, agc_en, infoTx): global FIG_LEN, pkt_count, ax6 pkt_count = pkt_count + 1 # Init n_ofdm_syms = ofdm_params[0] cp_len = ofdm_params[1] data_cp_len = ofdm_params[2] num_sc = ofdm_params[3] mod_order = ofdm_params[4] fft_offset = ofdm_params[5] tx_payload = tx_struct[0] payload_len = len(tx_struct[0]) data_const = tx_struct[1] sc_idx_all = tx_struct[2] tx_data = tx_struct[3] txSignal = tx_struct[4] lts_syms_len = len(tx_struct[5]) lts_freq = tx_struct[6] pilots_matrix = tx_struct[8] data_sc = sc_idx_all[0] pilot_sc = sc_idx_all[1] n_data_syms = n_ofdm_syms * len(data_sc) # Read samples into this buffer sampsRx = [np.zeros(num_samps_rd, np.complex64), np.zeros(num_samps_rd, np.complex64)] buff0 = sampsRx[0] # RF Chain 1 buff1 = sampsRx[1] # RF Chain 2 if ota: # Over-the-air Mode #find_optimal_gain(sdrTx, sdr) flags = SOAPY_SDR_END_BURST flags |= SOAPY_SDR_WAIT_TRIGGER sdr.activateStream(rxStream, flags, # flags 0, # timeNs (dont care unless using SOAPY_SDR_HAS_TIME) buff0.size) # numElems - this is the burst size if agc_en and "CBRS" in infoRx["frontend"]: sdr.writeRegister("IRIS30", FPGA_IRIS030_WR_AGC_RESET_FLAG, 1) sdr.writeRegister("IRIS30", FPGA_IRIS030_WR_AGC_RESET_FLAG, 0) sdr.writeRegister("IRIS30", FPGA_IRIS030_WR_PKT_DET_NEW_FRAME, 1) sdr.writeRegister("IRIS30", FPGA_IRIS030_WR_PKT_DET_NEW_FRAME, 0) sdr.writeSetting("TRIGGER_GEN", "") sr = sdr.readStream(rxStream, [buff0, buff1], buff0.size) if sr.ret != buff0.size: print("Read RX burst of %d, requested %d" % (sr.ret, buff0.size)) # Retrieve RSSI computed in the FPGA rssi_fpga = int(sdr.readRegister("IRIS30", FPGA_IRIS030_RD_MEASURED_RSSI)) # RX if "CBRS" in infoTx["frontend"]: lna_rd = sdr.getGain(SOAPY_SDR_RX, 0, 'LNA') # ChanA (0) tia_rd = sdr.getGain(SOAPY_SDR_RX, 0, 'TIA') # ChanA (0) pga_rd = sdr.getGain(SOAPY_SDR_RX, 0, 'PGA') # ChanA (0) lna1_rd = sdr.getGain(SOAPY_SDR_RX, 0, 'LNA1') # ChanA (0) lna2_rd = sdr.getGain(SOAPY_SDR_RX, 0, 'LNA2') # ChanA (0) attn_rd1 = sdr.getGain(SOAPY_SDR_RX, 0, 'ATTN') # ChanA (0) print("LNA: {} \t TIA: {} \t PGA: {} \t LNA1: {} \t LNA2: {} \t ATTN1: {}".format(lna_rd, tia_rd, pga_rd, lna1_rd, lna2_rd, attn_rd1)) else: lna_rd = sdr.getGain(SOAPY_SDR_RX, 0, 'LNA') # ChanA (0) tia_rd = sdr.getGain(SOAPY_SDR_RX, 0, 'TIA') # ChanA (0) pga_rd = sdr.getGain(SOAPY_SDR_RX, 0, 'PGA') # ChanA (0) print("LNA: {} \t TIA: {} \t PGA: {}".format(lna_rd, tia_rd, pga_rd)) else: # Simulation Mode sampsRx[0] = txSignal + 0.01 * (np.random.randn(len(txSignal)) + np.random.randn(len(txSignal)) * 1j) sampsRx[1] = txSignal + 0.01 * (np.random.randn(len(txSignal)) + np.random.randn(len(txSignal)) * 1j) # DC removal for i in [0, 1]: sampsRx[i] -= np.mean(sampsRx[i]) # Find LTS peaks (in case LTSs were sent) lts_thresh = 0.8 a, b, peaks0 = find_lts(sampsRx[0], thresh=lts_thresh, flip=True) a, b, peaks1 = find_lts(sampsRx[1], thresh=lts_thresh, flip=True) # Check if LTS found if not a: print("SISO_OFDM: No LTS Found!") return line1, line2, line3, line4, line5, line6, line7, line8, line9, line10, line11, line12 # If beginning of frame was not captured in current buffer if (a - lts_syms_len) < 0: print("TOO EARLY... CONTINUE! ") return line1, line2, line3, line4, line5, line6, line7, line8, line9, line10, line11, line12 # Decode signal (focus on RF chain A only for now) rxSignal = sampsRx[0] payload_start = a + 1 payload_end = payload_start + payload_len - 1 # Payload_len == (n_ofdm_syms * (num_sc + data_cp_len)) lts_start = a - lts_syms_len + 1 # where LTS-CP start # Apply CFO Correction if APPLY_CFO_CORR: coarse_cfo_est = ofdm_obj.cfo_correction(rxSignal, lts_start, lts_syms_len, fft_offset) else: coarse_cfo_est = 0 correction_vec = np.exp(-1j * 2 * np.pi * coarse_cfo_est * np.array(range(0, len(rxSignal)))) rxSignal_cfo = rxSignal * correction_vec # Channel estimation # Get LTS again (after CFO correction) lts = rxSignal_cfo[lts_start: lts_start + lts_syms_len] # Verify number of samples if len(lts) != 160: print("INCORRECT START OF PAYLOAD... CONTINUE!") return line1, line2, line3, line4, line5, line6, line7, line8, line9, line10, line11, line12 lts_1 = lts[-64 + -fft_offset + np.array(range(97, 161))] lts_2 = lts[-fft_offset + np.array(range(97, 161))] # Average 2 LTS symbols to compute channel estimate tmp = np.fft.ifftshift(lts_freq) chan_est = np.fft.ifftshift(lts_freq) * (np.fft.fft(lts_1) + np.fft.fft(lts_2))/2 # Assert sample position # NOTE: If packet found is not fully captured in current buffer read, ignore it and continue... if len(rxSignal_cfo) >= payload_end: # Retrieve payload symbols payload_samples = rxSignal_cfo[payload_start: payload_end + 1] else: print("TOO LATE... CONTINUE! ") return line1, line2, line3, line4, line5, line6, line7, line8, line9, line10, line11, line12 # Assert if len(payload_samples) != ((num_sc + data_cp_len) * n_ofdm_syms): print("INCORRECT START OF PAYLOAD... CONTINUE!") return line1, line2, line3, line4, line5, line6, line7, line8, line9, line10, line11, line12 else: payload_samples_mat_cp = np.reshape(payload_samples, ((num_sc + data_cp_len), n_ofdm_syms), order="F") # Remove cyclic prefix payload_samples_mat = payload_samples_mat_cp[data_cp_len - fft_offset + 1 + np.array(range(0, num_sc)), :] # FFT rxSig_freq = np.fft.fft(payload_samples_mat, n=num_sc, axis=0) # Equalizer chan_est_tmp = chan_est.reshape(len(chan_est), 1, order="F") rxSig_freq_eq = rxSig_freq / np.matlib.repmat(chan_est_tmp, 1, n_ofdm_syms) # Apply SFO Correction if APPLY_SFO_CORR: rxSig_freq_eq = ofdm_obj.sfo_correction(rxSig_freq_eq, pilot_sc, pilots_matrix, n_ofdm_syms) else: sfo_corr = np.zeros((num_sc, n_ofdm_syms)) # Apply phase correction if APPLY_PHASE_CORR: phase_error = ofdm_obj.phase_correction(rxSig_freq_eq, pilot_sc, pilots_matrix) else: phase_error = np.zeros((1, n_ofdm_syms)) phase_corr_tmp = np.matlib.repmat(phase_error, num_sc, 1) phase_corr = np.exp(-1j * phase_corr_tmp) rxSig_freq_eq_phase = rxSig_freq_eq * phase_corr rxSymbols_mat = rxSig_freq_eq_phase[data_sc, :] # Demodulation rxSymbols_vec = np.reshape(rxSymbols_mat, n_data_syms, order="F") # Reshape into vector rx_data = ofdm_obj.demodulation(rxSymbols_vec, mod_order) print(" === STATS ===") symbol_err = np.sum(tx_data != rx_data) print("Frame#: {} --- Symbol Errors: {} out of {} total symbols".format(pkt_count, symbol_err, n_data_syms)) # PLOTTING SECTION rx_H_est_plot = np.squeeze(np.matlib.repmat(complex('nan'), 1, len(chan_est))) rx_H_est_plot[data_sc] = np.squeeze(chan_est[data_sc]) rx_H_est_plot[pilot_sc] = np.squeeze(chan_est[pilot_sc]) x_ax = (20 / num_sc) * np.array(range(-(num_sc // 2), (num_sc // 2))) # add 5 on each side for visualization # Fill out data structures with measured data line1.set_data(range(len(txSignal)), np.abs(txSignal)) line2.set_data(range(len(rxSignal)), np.real(rxSignal)) line3.set_data(range(len(rxSignal)), np.imag(rxSignal)) line4.set_data(range(len(rxSignal)), np.abs(rxSignal)) line5.set_data(range(len(peaks0)), np.abs(peaks0)) line6.set_data(np.real(data_const), np.imag(data_const)) line7.set_data(np.real(rxSymbols_mat), np.imag(rxSymbols_mat)) line8.set_data(payload_start * np.ones(100), np.linspace(0.0, 1.0, num=100)) line9.set_data(payload_end * np.ones(100), np.linspace(0.0, 1.0, num=100)) line10.set_data((a - 2*lts_syms_len + 1) * np.ones(100), np.linspace(0.0, 1.0, num=100)) line11.set_data(np.linspace(0.0, FIG_LEN, num=1000), (lts_thresh * np.max(peaks0)) * np.ones(1000)) line12.set_data(x_ax, np.fft.fftshift(abs(rx_H_est_plot))) return line1, line2, line3, line4, line5, line6, line7, line8, line9, line10, line11, line12
def init(hub, bnodes, cnodes, ref_ant, ampl, rate, freq, txgain, rxgain, cp, plotter, numSamps, prefix_length, postfix_length, tx_advance, mod_order, threshold, use_trig): if hub != "": hub_dev = SoapySDR.Device(dict(driver="remote", serial = hub)) # device that triggers bnodes and ref_node bsdrs = [SoapySDR.Device(dict(driver="iris", serial = serial)) for serial in bnodes] # base station sdrs csdrs = [SoapySDR.Device(dict(driver="iris", serial = serial)) for serial in cnodes] # client sdrs # assume trig_sdr is part of the master nodes trig_dev = None if hub != "": trig_dev = hub_dev else: trig_dev = bsdrs[0] #set params on both channels for sdr in bsdrs+csdrs: info = sdr.getHardwareInfo() print("%s settings on device" % (info["frontend"])) for ch in [0, 1]: sdr.setBandwidth(SOAPY_SDR_TX, ch, 2.5*rate) sdr.setBandwidth(SOAPY_SDR_RX, ch, 2.5*rate) sdr.setSampleRate(SOAPY_SDR_TX, ch, rate) sdr.setSampleRate(SOAPY_SDR_RX, ch, rate) # sdr.setFrequency(SOAPY_SDR_TX, ch, freq) # sdr.setFrequency(SOAPY_SDR_RX, ch, freq) sdr.setFrequency(SOAPY_SDR_TX, ch, 'RF', freq-.75*rate) sdr.setFrequency(SOAPY_SDR_RX, ch, 'RF', freq-.75*rate) sdr.setFrequency(SOAPY_SDR_TX, ch, 'BB', .75*rate) sdr.setFrequency(SOAPY_SDR_RX, ch, 'BB', .75*rate) sdr.setAntenna(SOAPY_SDR_RX, ch, "TRX") sdr.setDCOffsetMode(SOAPY_SDR_RX, ch, True) sdr.setGain(SOAPY_SDR_TX, ch, 'PAD', txgain) sdr.setGain(SOAPY_SDR_RX, ch, 'LNA', rxgain) if "CBRS" in info["frontend"]: sdr.setGain(SOAPY_SDR_TX, ch, 'ATTN', -6) sdr.setGain(SOAPY_SDR_RX, ch, 'LNA2', 14) if freq < 3e9: sdr.setGain(SOAPY_SDR_RX, ch, 'ATTN', -12) else: sdr.setGain(SOAPY_SDR_RX, ch, 'ATTN', 0) # Read initial gain settings readLNA = sdr.getGain(SOAPY_SDR_RX, 0, 'LNA') readTIA = sdr.getGain(SOAPY_SDR_RX, 0, 'TIA') readPGA = sdr.getGain(SOAPY_SDR_RX, 0, 'PGA') print("INITIAL GAIN - LNA: {}, \t TIA:{}, \t PGA:{}".format(readLNA, readTIA, readPGA)) sdr.writeSetting("RESET_DATA_LOGIC", "") sdr.writeSetting(SOAPY_SDR_RX, 1, 'ENABLE_CHANNEL', 'false') sdr.writeSetting(SOAPY_SDR_TX, 1, 'ENABLE_CHANNEL', 'false') trig_dev.writeSetting("SYNC_DELAYS", "") sym_samps = numSamps + prefix_length + postfix_length print("numSamps = %d"%sym_samps) M = len(bsdrs) K = len(csdrs) N = 64 D = 1 # number of downlink symbols pad1 = np.array([0]*(prefix_length), np.complex64) # to comprensate for front-end group delay pad2 = np.array([0]*(postfix_length), np.complex64) # to comprensate for rf path delay wbz = np.array([0]*(sym_samps), np.complex64) # OFDM object ofdm_obj = ofdmTxRx() #### Generate Pilot cp_len = 32 if cp else 0 ofdm_len = 2*N + cp_len lts_rep = numSamps//(ofdm_len) zeros = np.array([0]*(numSamps-ofdm_len)) lts_sym, lts_f = generate_training_seq(preamble_type='lts', cp=cp_len, upsample=1) pilot = np.concatenate((lts_sym, zeros)) wb_pilot = ampl * pilot wb_pilot1 = np.concatenate([pad1, wb_pilot, pad2]) lts_t = lts_sym[-64:] lts_t_cp = np.concatenate((lts_t[len(lts_t) - 16:], lts_t)) #### Generate Beacon and hadamard weights upsample = 1 preambles_bs = generate_training_seq(preamble_type='gold_ifft', seq_length=128, cp=0, upsample=upsample) preambles = preambles_bs[:,::upsample] beacon = preambles[0,:] coe = cfloat2uint32(np.conj(beacon), order='QI') bcnz = np.array([0]*(sym_samps-prefix_length-len(beacon)), np.complex64) beacon1 = np.concatenate([pad1,beacon*.5,bcnz]) beacon2 = wbz possible_dim = [] possible_dim.append(2**(np.ceil(np.log2(M)))) h_dim = min(possible_dim) hadamard_matrix = hadamard(h_dim) beacon_weights = hadamard_matrix[0:M, 0:M] beacon_weights = beacon_weights.astype(np.uint32) #### Generate Data data_cp_len = 16 if cp else 0 data_ofdm_len = N + data_cp_len n_ofdm_syms = (numSamps // data_ofdm_len) sig_t, data_const, tx_data, sc_idx_all, pilots_matrix = \ ofdm_obj.generate_data(n_ofdm_syms - 2, mod_order, cp_length=data_cp_len) data_sc = sc_idx_all[0] pilot_sc = sc_idx_all[1] tx_dl_data = np.zeros((N, n_ofdm_syms, K)).astype(complex) for k in range(K): tx_dl_data[data_sc, 2:, k] = data_const tx_dl_data[pilot_sc, 2:, k] = pilots_matrix tx_dl_data[:, 0, k] = lts_f tx_dl_data[:, 1, k] = lts_f tx_dl_ifft = np.zeros((M, n_ofdm_syms, N)).astype(complex) print("n_ofdm_syms %d, data_ofdm_len %d"%(n_ofdm_syms, data_ofdm_len)) # received data params lts_thresh = 0.8 n_data_ofdm_syms = n_ofdm_syms - 2 payload_len = n_data_ofdm_syms * data_ofdm_len lts_len = 2 * data_ofdm_len fft_offset = 0 #### Configure tdd mode guardSize = (len(csdrs)) % 2 + 1 frameLen = len(csdrs) + len(bsdrs)*2 + 4 + guardSize # BS frame config for i,sdr in enumerate(bsdrs): beacon_sch = "BG" if i == ref_ant: ref_ul_pilot_sch = "PG" ref_dl_pilot_sch = ''.join("RG" * (M - 1)) ul_pilot_sch = ''.join("R" * K) else: ref_ul_pilot_sch = "RG" new_i = i - (i > ref_ant) ref_dl_pilot_sch = ''.join("GG" * new_i) + "PG" + ''.join("GG" * (M-(new_i+2))) ul_pilot_sch = ''.join("R" * K) frame_sch1 = beacon_sch + ref_ul_pilot_sch + ref_dl_pilot_sch + ul_pilot_sch + 'G' dl_data_sch = "PG" + ''.join("G" * (2 * M + K - (2 * D))) frame_sch2 = beacon_sch + dl_data_sch + 'G' print("BS node %d frame schedule (%s, %s)" % (i, frame_sch1, frame_sch2)) bconf = {"tdd_enabled": True, "frame_mode": "triggered", "symbol_size" : sym_samps, "frames": [frame_sch1, frame_sch2], "beacon_start" : prefix_length, "beacon_stop" : prefix_length+len(beacon), "max_frame" : 2} sdr.writeSetting("TDD_CONFIG", json.dumps(bconf)) sdr.writeSetting("TDD_MODE", "true") # Client frame config for i, sdr in enumerate(csdrs): det_sch = "GG" ref_pilot_sch = ''.join("GG" * M) ul_pilot_sch = ''.join("G" * i) + "P" + ''.join("G" * (K-(i+1))) frame_sch1 = det_sch + ref_pilot_sch + ul_pilot_sch + 'G' dl_data_sch = "RG" * D + ''.join("G" * (2 * M + K - (2 * D))) frame_sch2 = det_sch + dl_data_sch + 'G' print("Client %d frame schedule (%s, %s)"%(i, frame_sch1, frame_sch2)) cconf = {"tdd_enabled": True, "frame_mode": "triggered", "symbol_size" : sym_samps, "frames": [frame_sch1, frame_sch2], "max_frame" : 0} sdr.writeSetting("TDD_CONFIG", json.dumps(cconf)) sdr.writeSetting("TDD_MODE", "true") for sdr in bsdrs+csdrs: sdr.writeSetting("TX_SW_DELAY", str(30)) if not use_trig: for sdr in csdrs: # enable the correlator, with zeros as inputs corr_conf = {"corr_enabled" : True, "corr_threshold" : 1} sdr.writeSetting("CORR_CONFIG", json.dumps(corr_conf)) sdr.writeRegisters("CORR_COE", 0, coe.tolist()) # DEV: ueTrigTime = 153 (prefix_length=0), # CBRS: ueTrigTime = 235 (prefix_length=82), tx_advance=prefix_length, # corr delay is 17 cycles #cl_trig_time = prefix_length + len(beacon) + postfix_length + 17 + postfix_length cl_trig_time = 256 + 250 sf_start = cl_trig_time // sym_samps sp_start = cl_trig_time % sym_samps print("UE starting symbol and sample count (%d, %d)" % (sf_start, sp_start)) # make sure to set this after TDD mode is enabled "writeSetting("TDD_CONFIG", ..." sdr.setHardwareTime(SoapySDR.ticksToTimeNs((sf_start << 16) | sp_start, rate), "TRIGGER") else: for sdr in csdrs: sdr.setHardwareTime(0, "TRIGGER") for i,sdr in enumerate(bsdrs): sdr.setHardwareTime(0, "TRIGGER") replay_addr = 0 pilot_uint = cfloat2uint32(wb_pilot1, order='QI').tolist() beacon_uint = cfloat2uint32(beacon1, order='QI').tolist() zero_uint = cfloat2uint32(wbz, order='QI').tolist() for i, sdr in enumerate(bsdrs): sdr.writeRegisters("BEACON_RAM", 0, beacon_uint) sdr.writeRegisters("BEACON_RAM_WGT_A", 0, beacon_weights[i].tolist()) sdr.writeSetting("BEACON_START", str(M)) for sdr in csdrs: sdr.writeRegisters("TX_RAM_A", replay_addr, pilot_uint) sdr.writeRegisters("TX_RAM_B", replay_addr, zero_uint) # Create and activate streams rx_stream_ul = [sdr.setupStream(SOAPY_SDR_RX, SOAPY_SDR_CF32, [0, 1]) for sdr in bsdrs] rx_stream_dl = [sdr.setupStream(SOAPY_SDR_RX, SOAPY_SDR_CF32, [0, 1]) for sdr in csdrs] flags = 0 for i, sdr in enumerate(bsdrs): sdr.activateStream(rx_stream_ul[i], flags, 0) for i, sdr in enumerate(csdrs): sdr.activateStream(rx_stream_dl[i], flags, 0) ############# #initialize array and matrixes to hold rx and processed data ############# calib_rx_dl = [np.array([1]*sym_samps).astype(np.complex64) for m in range(M)] calib_rx_ul = [np.array([1]*sym_samps).astype(np.complex64) for m in range(M)] data_rx_dl = [[np.array([0]*sym_samps).astype(np.complex64) for k in range(K)] for m in range(D)] pilot_rx_ul = [[np.array([0]*sym_samps).astype(np.complex64) for m in range(M)] for k in range(K)] dummy_rx = np.array([0]*sym_samps).astype(np.complex64) ul_cal_offset = np.array([0]*M, np.int32) dl_cal_offset = np.array([0]*M, np.int32) ul_offset = [np.array([0]*K, np.int32) for m in range(M)] dl_offset = [np.array([0]*K, np.int32) for m in range(M)] ul_csi_mat = np.empty([K, M, N], dtype=np.complex64) rx_f_cal_dl = np.empty([M, N], dtype=np.complex64) rx_f_cal_ul = np.empty([M, N], dtype=np.complex64) w_zf_dl = np.empty([M, K, N], dtype=np.complex64) w_conj_dl = np.empty([M, K, N], dtype=np.complex64) calib = np.empty((M, N)).astype(np.complex64) rxSymbols_mat = np.empty([len(data_sc), D * n_data_ofdm_syms, K], dtype=np.complex64) cont_plotter = plotter if cont_plotter: fig1, axes1 = plt.subplots(nrows=M, ncols=2, figsize=(9, 12)) axes1[0, 0].set_title('Pilot Uplink (Re)') axes1[0, 1].set_title('Pilot Uplink (Im)') for m in range(M): axes1[m, 0].set_xlim(0, sym_samps) axes1[m, 0].set_ylim(-1, 1) if m == ref_ant: axes1[m, 0].set_ylabel('Ant %d (ref)'%m) else: axes1[m, 0].set_ylabel('Ant %d'%m) axes1[m, 0].legend(fontsize=10) axes1[m, 1].set_xlim(0, sym_samps) axes1[m, 1].set_ylim(-1, 1) axes1[m, 1].legend(fontsize=10) lines11 = [[axes1[m, 0].plot(range(sym_samps), np.real(pilot_rx_ul[k][m]), label="User %d (real)"%k)[0] for k in range(K)] for m in range(M)] lines12 = [[axes1[m, 1].plot(range(sym_samps), np.imag(pilot_rx_ul[k][m]), label="User %d (imag)"%k)[0] for k in range(K)] for m in range(M)] fig1.show() fig2, axes2 = plt.subplots(nrows=M, ncols=2, figsize=(9, 12)) axes2[0, 0].set_title('Calibration Downlink') axes2[0, 1].set_title('Calibration Uplink') for m in range(M): axes2[m, 0].set_xlim(0, sym_samps) axes2[m, 0].set_ylim(-1, 1) if m == ref_ant: axes2[m, 0].set_ylabel('Ant %d (ref)'%m) else: axes2[m, 0].set_ylabel('Ant %d'%m) axes2[m, 0].legend(fontsize=10) axes2[m, 1].set_xlim(0, sym_samps) axes2[m, 1].set_ylim(-1, 1) axes2[m, 1].legend(fontsize=10) lines20 = [axes2[m, 0].plot(range(sym_samps), calib_rx_dl[m][:sym_samps])[0] for m in range(M)] lines21 = [axes2[m, 1].plot(range(sym_samps), calib_rx_ul[m][:sym_samps])[0] for m in range(M)] lines24 = [axes2[m, 0].plot(range(sym_samps), calib_rx_dl[m][:sym_samps])[0] for m in range(M)] lines25 = [axes2[m, 1].plot(range(sym_samps), calib_rx_ul[m][:sym_samps])[0] for m in range(M)] fig2.show() fig3, axes3 = plt.subplots(nrows=K, ncols=1, figsize=(6, 6)) for k in range(K): if K == 1: ax3 = axes3 else: ax3 = axes3[k] ax3.grid(True) ax3.set_title('TX/RX Constellation') ax3.set_xlabel('') ax3.set_ylabel('') ax3.set_ylim(-5.5, 5.5) ax3.set_xlim(-5.8, 5.8) ax3.legend(fontsize=10) if K == 1: line31, = axes3.plot([], [], 'ro', label='TXSym') line32, = axes3.plot([], [], 'bx', label='RXSym') else: line31 = [axes3[k].plot([], [], 'ro', label='TXSym')[0] for k in range(K)] line32 = [axes3[k].plot([], [], 'bx', label='RXSym')[0] for k in range(K)] fig3.show() fig4, axes4 = plt.subplots(nrows=K, ncols=1, figsize=(6, 6)) for k in range(K): if K == 1: ax4 = axes4 else: ax4 = axes4[k] ax4.grid(True) ax4.set_title('Received Downlink') ax4.set_xlabel('') ax4.set_ylabel('') ax4.set_ylim(-1, 1) ax4.set_xlim(0, sym_samps) ax4.legend(fontsize=10) if K == 1: line41, = axes4.plot(range(sym_samps), data_rx_dl[0][0], label='RX Downlink') line42, = axes4.plot(range(sym_samps), data_rx_dl[0][0]) else: line41 = [axes4[k].plot(range(sym_samps), data_rx_dl[0][k], label='RX Downlink')[0] for k in range(K)] line42 = [axes4[k].plot(range(sym_samps), data_rx_dl[0][k])[0] for k in range(K)] fig4.show() cur_frame = 0 signal.signal(signal.SIGINT, signal_handler) tstart = datetime.datetime.now() while(RUNNING): ## disarm correlator in the clients if not use_trig: for i, sdr in enumerate(csdrs): sdr.writeSetting("CORR_CONFIG", json.dumps({"corr_enabled": False})) bad_recip_read = False bad_frame = False ## arm correlator in the clients, inputs from adc if not use_trig: for i, sdr in enumerate(csdrs): sdr.writeSetting("CORR_START", "A") for i, sdr in enumerate(bsdrs): sdr.writeRegisters("TX_RAM_A", 0, pilot_uint) sdr.writeRegisters("TX_RAM_B", 0, zero_uint) trig_dev.writeSetting("TRIGGER_GEN", "") ## collect reciprocity pilots from antenna m for m in range(M): if bad_recip_read: break if m != ref_ant: sr = bsdrs[m].readStream(rx_stream_ul[m], [calib_rx_ul[m], dummy_rx], sym_samps) if sr.ret < sym_samps: print("Calib: m %d ret %d"%(m,sr.ret)) bad_recip_read = True for m in range(M): if bad_recip_read: break if m != ref_ant: sr = bsdrs[ref_ant].readStream(rx_stream_ul[ref_ant], [calib_rx_dl[m], dummy_rx], sym_samps) if sr.ret < sym_samps: print("Calib: m %d ret %d"%(m,sr.ret)) bad_recip_read = True if bad_recip_read: print("BAD RECIPROCAL PILOT READ... CONTINUE! ") continue ## collect uplink pilots bad_pilot_read = False for k in range(K): if bad_pilot_read: break for m in range(M): sr = bsdrs[m].readStream(rx_stream_ul[m], [pilot_rx_ul[k][m], dummy_rx], sym_samps) if sr.ret < sym_samps: print("PilotUP: k: %d, m %d ret %d"%(k,m,sr.ret)) bad_pilot_read = True if bad_pilot_read: print("BAD PILOT READ... CONTINUE! ") continue ## process downlink signal # processing the received calibration samples print("frame %d"%(cur_frame)) for m in range(M): if ref_ant == m: calib[m,:] = np.array([1]*N, np.complex64) #continue rx_f_cal_dl[m,:] = np.array([0]*N, np.complex64) rx_f_cal_ul[m,:] = np.array([0]*N, np.complex64) continue calib_rx_dl[m] -= np.mean(calib_rx_dl[m]) calib_rx_ul[m] -= np.mean(calib_rx_ul[m]) best_peak_dl, _, _ = find_lts(calib_rx_dl[m], thresh=LTS_THRESH) best_peak_ul, _, _ = find_lts(calib_rx_ul[m], thresh=LTS_THRESH) dl_cal_offset[m] = 0 if not best_peak_dl else best_peak_dl - len(lts_sym) + cp_len ul_cal_offset[m] = 0 if not best_peak_ul else best_peak_ul - len(lts_sym) + cp_len if (dl_cal_offset[m] < 150 or ul_cal_offset[m] < 150): bad_frame = True lts_dn_1 = calib_rx_dl[m][dl_cal_offset[m]:dl_cal_offset[m]+N] lts_dn_2 = calib_rx_dl[m][dl_cal_offset[m]+N:dl_cal_offset[m]+2*N] lts_up_1 = calib_rx_ul[m][ul_cal_offset[m]:ul_cal_offset[m]+N] lts_up_2 = calib_rx_ul[m][ul_cal_offset[m]+N:ul_cal_offset[m]+2*N] rx_f_cal_dl1 = np.fft.fftshift(np.fft.fft(lts_dn_1, N, 0), 0) rx_f_cal_dl2 = np.fft.fftshift(np.fft.fft(lts_dn_2, N, 0), 0) rx_f_cal_ul1 = np.fft.fftshift(np.fft.fft(lts_up_1, N, 0), 0) rx_f_cal_ul2 = np.fft.fftshift(np.fft.fft(lts_up_2, N, 0), 0) rx_f_cal_dl[m, :] = (rx_f_cal_dl1 + rx_f_cal_dl2)/2 rx_f_cal_ul[m, :] = (rx_f_cal_ul1 + rx_f_cal_ul2)/2 calib[m,:] = np.divide(rx_f_cal_dl[m,:], rx_f_cal_ul[m,:]) # processing uplink pilot received samples for k in range(K): for m in range(M): pilot_rx_ul[k][m] -= np.mean(pilot_rx_ul[k][m]) best_peak, _, _ = find_lts(pilot_rx_ul[k][m], thresh=LTS_THRESH) ul_offset[m][k] = 0 if not best_peak else (best_peak - len(lts_sym) + cp_len) if ul_offset[m][k] < 150: bad_frame = True lts_ul_1 = pilot_rx_ul[k][m][ul_offset[m][k]:ul_offset[m][k]+N] lts_ul_2 = pilot_rx_ul[k][m][ul_offset[m][k]+N:ul_offset[m][k]+2*N] rx_f_ul1 = np.fft.fftshift(np.fft.fft(lts_ul_1, N, 0), 0) rx_f_ul2 = np.fft.fftshift(np.fft.fft(lts_ul_2, N, 0), 0) ul_csi_mat[k,m,:] = (rx_f_cal_ul1 + rx_f_cal_ul2) * lts_f / 2 # processing beamforming vectors and downlink transmit data for l in range(n_ofdm_syms): for n in range(N): dl_csi = np.matmul(ul_csi_mat[:, :, n], np.diag(calib[:, n])) w_zf_dl[:, :, n] = np.linalg.pinv(dl_csi) tx_dl_ifft[:, l, n] = np.matmul(w_zf_dl[:, :, n], tx_dl_data[n, l, :]) tx_sym = np.fft.ifft(tx_dl_ifft, axis=2) tx_sym = np.concatenate((tx_sym[:,:,-data_cp_len:], tx_sym), axis=2) # send downlink signal for i, sdr in enumerate(bsdrs): tx_sig = np.reshape(tx_sym[i, :, :], (1, n_ofdm_syms * data_ofdm_len)) tx_sig = np.concatenate([pad1, tx_sig[0, :], pad2]) sdr.writeRegisters("TX_RAM_A", 0, cfloat2uint32(tx_sig, order='QI').tolist()) sdr.writeRegisters("TX_RAM_B", 0, zero_uint) trig_dev.writeSetting("TRIGGER_GEN", "") # collect downlink data from antenna k bad_dl_read = False for d in range(D): if bad_dl_read: break for k in range(K): sr = csdrs[k].readStream(rx_stream_dl[k], [data_rx_dl[d][k], dummy_rx], sym_samps) if sr.ret < sym_samps: print("DL DATA: symbol %d, k %d, ret %d"%(d, k, sr.ret)) bad_dl_read = True if bad_dl_read: print("BAD DL READ... CONTINUE! ") continue # DC removal # Find LTS peaks (in case LTSs were sent) bad_dl_data = False for k in range(K): if bad_dl_data: break for d in range(D): data_rx_dl[d][k] -= np.mean(data_rx_dl[d][k]) best_peak_dl, b, peaks0 = find_lts(data_rx_dl[d][k], thresh=lts_thresh, flip=True, lts_seq=lts_t_cp) if use_trig: dl_offset[k] = 163 + 160 #0 if not best_peak_dl else best_peak_dl else: dl_offset[k] = 0 if not best_peak_dl else best_peak_dl if dl_offset[k] < lts_len: bad_dl_data = True print("NO VALID DOWNLINK... CONTINUE! ") break payload_start = dl_offset[k] payload_end = payload_start + payload_len # Payload_len == (n_ofdm_syms * (num_sc + data_cp_len)) lts_start = payload_start - lts_len # where LTS-CP start lts = data_rx_dl[d][k][lts_start: payload_start] if len(lts) < lts_len: print("BAD DOWNLINK PILOT... CONTINUE! ") bad_dl_data = True break lts_1 = lts[16 + -fft_offset + np.array(range(0, 64))] lts_2 = lts[96 + -fft_offset + np.array(range(0, 64))] # Average 2 LTS symbols to compute channel estimate tmp = np.fft.ifftshift(lts_f) chan_est = np.fft.ifftshift(lts_f) * (np.fft.fft(lts_1) + np.fft.fft(lts_2))/2 if len(data_rx_dl[d][k]) >= payload_end: # Retrieve payload symbols payload_samples = data_rx_dl[d][k][payload_start: payload_end] else: bad_dl_data = True print("TOO LATE (payload_end %d)... CONTINUE! "%payload_end) break payload_samples_mat_cp = np.reshape(payload_samples, (data_ofdm_len, n_data_ofdm_syms), order="F") # Remove cyclic prefix payload_samples_mat = payload_samples_mat_cp[data_cp_len - fft_offset + np.array(range(0, N)), :] # FFT rxSig_freq = np.fft.fft(payload_samples_mat, n=N, axis=0) # Equalizer chan_est_tmp = chan_est.reshape(len(chan_est), 1, order="F") rxSig_freq_eq = rxSig_freq / np.matlib.repmat(chan_est_tmp, 1, n_data_ofdm_syms) phase_error = ofdm_obj.phase_correction(rxSig_freq_eq, pilot_sc, pilots_matrix) phase_corr_tmp = np.matlib.repmat(phase_error, N, 1) phase_corr = np.exp(-1j * phase_corr_tmp) rxSig_freq_eq_phase = rxSig_freq_eq * phase_corr rxSymbols_mat[:, d * n_data_ofdm_syms : (d + 1) * n_data_ofdm_syms, k] = rxSig_freq_eq_phase[data_sc, :] if bad_dl_data: continue evm_mat = np.power(np.abs(rxSymbols_mat - np.tile(tx_dl_data[data_sc, 2:, :], (1, D, 1))), 2) evm_per_user = np.mean(np.reshape(evm_mat, (len(data_sc) * n_data_ofdm_syms * D, K)), axis=0) evm_per_user_db = 10 * np.log10(evm_per_user) print('EVM (dB) per user') print([ "{:2.2f}".format(x) for x in evm_per_user_db ]) print('') cur_frame += 1 if cur_frame >= TOT_FRAMES: break if cont_plotter: for m in range(M): if m == ref_ant: lines20[m].set_ydata(np.real(wb_pilot1)) lines21[m].set_ydata(np.real(wb_pilot1)) continue lines20[m].set_ydata(np.real(calib_rx_dl[m])) lines21[m].set_ydata(np.real(calib_rx_ul[m])) lines24[m].set_data(dl_cal_offset[m], np.linspace(-1.0, 1.0, num=100)) lines25[m].set_data(ul_cal_offset[m], np.linspace(-1.0, 1.0, num=100)) for m in range(M): for k in range(K): lines11[m][k].set_ydata(np.real(pilot_rx_ul[k][m])) lines12[m][k].set_ydata(np.imag(pilot_rx_ul[k][m])) if K == 1: txSyms_all = tx_dl_data[data_sc, 2:, 0].flatten() rxSyms_all = rxSymbols_mat[:, :, 0].flatten() line31.set_data(np.real(txSyms_all), np.imag(txSyms_all)) line32.set_data(np.real(rxSyms_all), np.imag(rxSyms_all)) line41.set_data(range(sym_samps), np.real(data_rx_dl[0][0])) line42.set_data(dl_offset[0], np.linspace(-1.0, 1.0, num=100)) else: for k in range(K): txSyms_all = tx_dl_data[data_sc, 2:, k].flatten() rxSyms_all = rxSymbols_mat[:, :, k].flatten() line31[k].set_data(np.real(txSyms_all), np.imag(txSyms_all)) line32[k].set_data(np.real(rxSyms_all), np.imag(rxSyms_all)) line41[k].set_data(range(sym_samps), np.real(data_rx_dl[0][k])) line42[k].set_data(dl_offset[k], np.linspace(-1.0, 1.0, num=100)) fig1.canvas.draw() fig1.show() fig2.canvas.draw() fig2.show() fig3.canvas.draw() fig3.show() fig4.canvas.draw() fig4.show() tend = datetime.datetime.now() c = tend - tstart print("Elapsed %d (secs)"%c.total_seconds()) # clear up fpga states tdd_conf = {"tdd_enabled" : False} corr_conf = {"corr_enabled" : False} for sdr in csdrs: if not use_trig: sdr.writeSetting("CORR_CONFIG", json.dumps(corr_conf)) for sdr in bsdrs+csdrs: sdr.writeSetting("TDD_CONFIG", json.dumps(tdd_conf)) sdr.writeSetting("TDD_MODE", "false") sdr.writeSetting("RESET_DATA_LOGIC", "") # close streams and exit for i, sdr in enumerate(bsdrs): sdr.closeStream(rx_stream_ul[i]) for i, sdr in enumerate(csdrs): sdr.closeStream(rx_stream_dl[i])
def animate(i): global sdr, rxStream, timeScale, sampsRx, fft_size, rate, num_samps, agc_fsm, IQmag, countMegd # read samples into this buffer buff0 = sampsRx[0] # RF Chain 1 buff1 = sampsRx[1] # RF Chain 2 (Ignore for now) sdr.activateStream( rxStream, SOAPY_SDR_END_BURST, # flags 0, # timeNs (dont care unless using SOAPY_SDR_HAS_TIME) buff0.size) # numElems - this is the burst size sr = sdr.readStream(rxStream, [buff0, buff1], buff0.size) # Re-assign rxSignal = sampsRx[0] # dc removal sampsRx[rxChan] -= np.mean(sampsRx[rxChan]) lts_thresh = 0.8 a, b, peaks = find_lts(sampsRx[0], thresh=lts_thresh) write_to_file("./data_out/rx0", sampsRx[0]) # Magnitude of IQ Samples - Real and imaginary I = np.real(rxSignal) Q = np.imag(rxSignal) IQmag = np.mean(np.sqrt(I**2 + Q**2)) # DIGITAL RSSI agc_avg = 7 # average over X number of samples. See LMS7 datasheet rssi, PWRdBm = getDigitalRSSI(sdr, agc_avg) PWRdB = PWRdBm - 30 # TIME DOMAIN POWER sigRms = np.sqrt(np.mean(rxSignal * np.conj(rxSignal))) sigPwr = np.real(sigRms)**2 sigPwr_dB = 10 * np.log10(sigPwr) sigPwr_dBm = 10 * np.log10(sigPwr / 1e-3) # POWER FFT # powerBins = log_power_fft(rxSignal, fft_size) # noiseFloor, _ = log_power_fft_analysis(powerBins, rate) f1, powerBins, noiseFloor, pks = fft_power(rxSignal, rate, num_bins=None, peak=1.0, scaling='density', peak_thresh=20) fftPower = bandpower(rxSignal, rate, 0, rate / 2) fftPower_dB = 10 * np.log10(fftPower) fftPower_dBm = 10 * np.log10(fftPower / 1e-3) # Circular buffer - continuously display data rssiPwrBuffer.append(PWRdBm) timePwrBuffer.append(sigPwr_dB) freqPwrBuffer.append(fftPower_dB) noisPwrBuffer.append(noiseFloor) # PRINT VALUES # 50kHz sinusoid print("DigitalRSSI: {} \t DigitalPwr: {} ".format(rssi, PWRdBm)) line1.set_data(range(buff0.size), np.real(rxSignal)) line2.set_data(range(buff0.size), np.imag(rxSignal)) line3.set_data(f1, powerBins) line4.set_data(range(len(rssiPwrBuffer)), rssiPwrBuffer) line5.set_data(range(len(timePwrBuffer)), timePwrBuffer) line6.set_data(range(len(freqPwrBuffer)), freqPwrBuffer) line7.set_data(range(len(noisPwrBuffer)), noisPwrBuffer) line8.set_data(range(buff0.size), np.real(peaks[:buff0.size])) return line1, line2, line3, line4, line5, line6, line7, line8