def setupTxReplay(self, name): self._txReplayWaveform = name #empty string to disable if not name: return [ iris.writeSetting("TX_REPLAY", "") for iris in self._writeIrises ][0] if name == "LTS": import lts samps = lts.genLTS() #TODO others def cfloat2uint32(arr): import numpy as np arr_i = (np.real(arr) * 32767).astype(np.uint16) arr_q = (np.imag(arr) * 32767).astype(np.uint16) return np.bitwise_or(arr_q, np.left_shift(arr_i.astype(np.uint32), 16)) samps = cfloat2uint32(samps) for iris in self._writeIrises: iris.writeRegisters('TX_RAM_A', 0, cfloat2uint32(samps).tolist()) for iris in self._writeIrises: iris.writeRegisters('TX_RAM_B', 0, cfloat2uint32(samps).tolist()) for iris in self._writeIrises: iris.writeSetting("TX_REPLAY", str(len(samps)))
def setupUE(args, serial, rate, freq, txgain, rxgain, numSamps, numSyms, pilotSymbol, txSymbol, txSymNum, threshold, tx_advance, prefix_length, postfix_length, cp, calibrate, both_channels, wait_trigger, auto_tx_gain): global msdr, txStreamM, rxStreamM msdr = SoapySDR.Device(dict(serial=serial)) #some default sample rates for sdr in [msdr]: info = sdr.getHardwareInfo(); print("%s settings" % info["frontend"]) for ch in [0,1]: 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) if ("CBRS" in info["frontend"]): sdr.setGain(SOAPY_SDR_TX, ch, 'ATTN', 0) #[-18,0] by 3 sdr.setGain(SOAPY_SDR_TX, ch, 'PA1', 15) #[0|15] sdr.setGain(SOAPY_SDR_TX, ch, 'PA2', 0) #[0|15] sdr.setGain(SOAPY_SDR_TX, ch, 'PA3', 30) #[0|30] if ("UHF" in info["frontend"]): sdr.setGain(SOAPY_SDR_TX, ch, 'ATTN', 0) #[-18,0] by 3 sdr.setGain(SOAPY_SDR_TX, ch, 'IAMP', 0) #[0,12] sdr.setGain(SOAPY_SDR_TX, ch, 'PAD', txgain) #[0,52] if ("CBRS" in info["frontend"]): sdr.setGain(SOAPY_SDR_RX, ch, 'ATTN', 0) #[-18,0] sdr.setGain(SOAPY_SDR_RX, ch, 'LNA1', 30) #[0,33] sdr.setGain(SOAPY_SDR_RX, ch, 'LNA2', 17) #[0,17] if ("UHF" in info["frontend"]): sdr.setGain(SOAPY_SDR_RX, ch, 'ATTN1', -6) #[-18,0] sdr.setGain(SOAPY_SDR_RX, ch, 'ATTN2', -12) #[-18,0] sdr.setGain(SOAPY_SDR_RX, ch, 'LNA', rxgain) #[0,30] sdr.setGain(SOAPY_SDR_RX, ch, 'TIA', 0) #[0,12] sdr.setGain(SOAPY_SDR_RX, ch, 'PGA', 0) #[-12,19] sdr.setAntenna(SOAPY_SDR_RX, ch, "TRX") #sdr.setBandwidth(SOAPY_SDR_TX, ch, 30e6) #sdr.setBandwidth(SOAPY_SDR_RX, ch, 30e6) sdr.setDCOffsetMode(SOAPY_SDR_RX, ch, True) if calibrate: for ch in [0,1]: sdr.writeSetting(SOAPY_SDR_RX, ch, "CALIBRATE", 'SKLK') sdr.writeSetting(SOAPY_SDR_TX, ch, "CALIBRATE", 'SKLK') #if not both_channels: # sdr.writeSetting("SPI_TDD_MODE", "SISO") # sdr.writeSetting(SOAPY_SDR_RX, 1, 'ENABLE_CHANNEL', 'false') # sdr.writeSetting(SOAPY_SDR_TX, 1, 'ENABLE_CHANNEL', 'false') #else: # sdr.writeSetting("SPI_TDD_MODE", "MIMO") sdr.writeRegister("IRIS30", RF_RST_REG, (1<<29) | 0x1) sdr.writeRegister("IRIS30", RF_RST_REG, (1<<29)) sdr.writeRegister("IRIS30", RF_RST_REG, 0) msdr.writeRegister("ARGCOR", CORR_RST, 0x1) # reset corr msdr.writeRegister("IRIS30", TX_GAIN_CTRL, 0) minfo = msdr.getHardwareInfo() #packet size symSamp = numSamps + prefix_length + postfix_length print("symSamp = %d"%symSamp) print("txSymNum = %d"%txSymNum) upsample = 1 # preambles to be sent from BS and correlated against in UE preambles_bs, highest_peak_to_second_ratio_bs = Preambles.getPreambles('gold_ifft', 128, 0, upsample=1) #the base station may upsample, but the mobiles won't preambles = preambles_bs[:,::upsample] #the correlators can run at lower rates, so we only need the downsampled signal. beacon = preambles[0,:]*.25 coe = cfloat2uint32(np.conj(beacon), order='QI') # FPGA correlator takes coefficients in QI order ltsSym = lts.genLTS(upsample=upsample,cp=1 if cp else 0) 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 = np.tile(ltsSym,numSamps/len(ltsSym)).astype(np.complex64)*.5 wbz = np.array([0]*(symSamp), np.complex64) wb_pilot1 = np.concatenate([pad1,wb_pilot,pad2]) wb_pilot2 = wb_pilot1 if both_channels else wbz Ts = 1/rate s_freq = 1e6 s_time_vals = np.array(np.arange(0,symSamp)).transpose()*Ts nb_data = np.exp(s_time_vals*1j*2*np.pi*s_freq).astype(np.complex64)*.25 rand_data = [1,0,1,1,0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, \ 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, \ 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 0 ,1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, \ 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1 ,0, 1, 0, 0, \ 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, \ 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 1, 0, \ 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1] #data_16qam = genDATA(np.random.randint(2, size=64*4),0) data_16qam = ofdm.genDATAQPSK(rand_data,0) if not cp: data_16qam[16:] wb_data = np.tile(data_16qam,numSamps/len(data_16qam)).astype(np.complex64)*.5 wb_data = np.concatenate([pad1,wb_data,pad2]) if txSymNum > 0: txStreamM = msdr.setupStream(SOAPY_SDR_TX, SOAPY_SDR_CF32, [0, 1]) rxStreamM = msdr.setupStream(SOAPY_SDR_RX, SOAPY_SDR_CS16, [0, 1]) msdr.writeRegister("IRIS30", CORR_CONF, int("00004001", 16)) # enable the correlator, with zeros as inputs for i in range(128): msdr.writeRegister("ARGCOE", i*4, 0) time.sleep(0.1) msdr.writeRegister("ARGCOR", CORR_THRESHOLD, int(threshold)) msdr.writeRegister("ARGCOR", CORR_RST, 0x1) # reset corr msdr.writeRegister("ARGCOR", CORR_RST, 0x0) # unrst corr for i in range(128): msdr.writeRegister( "ARGCOE", i*4, int(coe[i])) if auto_tx_gain: max_gain = int(txgain) min_gain = max(0, max_gain-15) gain_reg = 0xF000 | (max_gain & 0x3F) << 6 | (min_gain & 0x3F) print("gain reg 0x%X" % gain_reg) msdr.writeRegister("IRIS30", TX_GAIN_CTRL, gain_reg) # [15] en, [14] mode, [13:12] step, [11:6] stop, [5:0] start if both_channels: pilotSymNum = 2 msched = ''.join("G"*(pilotSymbol-2))+"PP" else: pilotSymNum = 1 msched = ''.join("G"*(pilotSymbol-2))+"P" if txSymNum > 0: msched += ''.join("G"*(txSymbol-pilotSymbol-pilotSymNum))+''.join("T"*txSymNum)+''.join("G"*(numSyms-txSymNum-txSymbol)) msched = "GR" + msched else: msched += ''.join("G"*(numSyms-pilotSymbol-pilotSymNum)) msched = "GG" + msched print("Client schedule %s " % msched) mconf = {"tdd_enabled": True, "trigger_out": True, "wait_trigger": True, "dual_pilot": both_channels, "symbol_size" : symSamp, "frames": [msched]} msdr.writeSetting("TDD_CONFIG", json.dumps(mconf)) # DEV: ueTrigTime = 153 (prefix_length=0), CBRS: ueTrigTime = 235 (prefix_length=82), tx_advance=prefix_length ueTrigTime = prefix_length + len(beacon) + postfix_length + 17 + tx_advance sf_start = ueTrigTime/symSamp sp_start = ueTrigTime%symSamp print("UE starting symbol and sample count (%d, %d)" % (sf_start, sp_start)) msdr.setHardwareTime(SoapySDR.ticksToTimeNs((sf_start<<16) | sp_start, rate),"TRIGGER") # make sure to set this after TDD mode is enabled "writeSetting("TDD_CONFIG", ..." for sdr in [msdr]: sdr.writeSetting("TX_SW_DELAY", str(30)) msdr.writeSetting("TDD_MODE", "true") replay_addr = 0 if both_channels: msdr.writeRegisters("TX_RAM_A", replay_addr+(pilotSymbol%2)*2048, cfloat2uint32(wb_pilot1, order='QI').tolist()) msdr.writeRegisters("TX_RAM_B", replay_addr+((pilotSymbol+1)%2)*2048, cfloat2uint32(wb_pilot1, order='QI').tolist()) else: msdr.writeRegisters("TX_RAM_A", replay_addr, cfloat2uint32(wb_pilot1, order='QI').tolist()) msdr.writeRegisters("TX_RAM_B", replay_addr, cfloat2uint32(wb_pilot2, order='QI').tolist()) #pdb.set_trace() msdr.writeRegister("IRIS30", CORR_CONF, int("00004011", 16)) # enable the correlator, with inputs from adc signal.signal(signal.SIGINT, partial(signal_handler, rate, numSyms, txSymNum)) if txSymNum>0: txth = threading.Thread(target=tx_thread, args=(msdr, rate, txStreamM, rxStreamM, wb_data, symSamp, numSyms, txSymNum, txSymbol)) txth.start() #signal.pause() num_trig = 0 while True: time.sleep(0.25) t = SoapySDR.timeNsToTicks(msdr.getHardwareTime(""),rate) >> 32 #trigger count is top 32 bits. print("%d new triggers, %d total" % (t - num_trig, t)) num_trig = t
def siso_sounder(serial1, serial2, rate, freq, txgain, rxgain, bw, numSamps, numSyms, txSymNum, threshold, tx_advance, prefix_length, postfix_length, both_channels, wait_trigger, calibrate, record, use_trig, auto_tx_gain): global bsdr, msdr, txStreamM, rxStreamB print("setting %s as eNB and %s as UE" % (serial1, serial2)) bsdr = SoapySDR.Device(dict(serial=serial1)) msdr = SoapySDR.Device(dict(serial=serial2)) #some default sample rates for i, sdr in enumerate([bsdr, msdr]): info = sdr.getHardwareInfo() print("%s settings on device %d" % (info["frontend"], i)) for ch in [0, 1]: 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) if ("CBRS" in info["frontend"]): sdr.setGain(SOAPY_SDR_TX, ch, 'ATTN', 0) #[-18,0] by 3 sdr.setGain(SOAPY_SDR_TX, ch, 'PA1', 15) #[0,15] sdr.setGain(SOAPY_SDR_TX, ch, 'PA2', 0) #[0,15] sdr.setGain(SOAPY_SDR_TX, ch, 'PA3', 30) #[0,30] sdr.setGain(SOAPY_SDR_TX, ch, 'IAMP', 12) #[0,12] sdr.setGain(SOAPY_SDR_TX, ch, 'PAD', txgain) #[-52,0] if ("CBRS" in info["frontend"]): sdr.setGain(SOAPY_SDR_RX, ch, 'ATTN', 0) #[-18,0] sdr.setGain(SOAPY_SDR_RX, ch, 'LNA1', 30) #[0,33] sdr.setGain(SOAPY_SDR_RX, ch, 'LNA2', 17) #[0,17] sdr.setGain(SOAPY_SDR_RX, ch, 'LNA', rxgain) #[0,30] sdr.setGain(SOAPY_SDR_RX, ch, 'TIA', 0) #[0,12] sdr.setGain(SOAPY_SDR_RX, ch, 'PGA', 0) #[-12,19] sdr.setAntenna(SOAPY_SDR_RX, ch, "TRX") #sdr.setBandwidth(SOAPY_SDR_RX, ch, bw) #sdr.setBandwidth(SOAPY_SDR_TX, ch, bw) sdr.setDCOffsetMode(SOAPY_SDR_RX, ch, True) for ch in [0, 1]: if calibrate: sdr.writeSetting(SOAPY_SDR_RX, ch, "CALIBRATE", 'SKLK') sdr.writeSetting(SOAPY_SDR_TX, ch, "CALIBRATE", '') sdr.writeRegister("IRIS30", RF_RST_REG, (1 << 29) | 0x1) sdr.writeRegister("IRIS30", RF_RST_REG, (1 << 29)) sdr.writeRegister("IRIS30", RF_RST_REG, 0) if not both_channels: sdr.writeSetting("SPI_TDD_MODE", "SISO") sdr.writeSetting(SOAPY_SDR_RX, 1, 'ENABLE_CHANNEL', 'false') sdr.writeSetting(SOAPY_SDR_TX, 1, 'ENABLE_CHANNEL', 'false') else: sdr.writeSetting("SPI_TDD_MODE", "MIMO") #pdb.set_trace() msdr.writeRegister("IRIS30", TX_GAIN_CTRL, 0) if use_trig: bsdr.writeSetting("SYNC_DELAYS", "") #bsdr.writeSetting("FPGA_DIQ_MODE", "PATTERN") else: msdr.writeRegister("ARGCOR", CORR_RST, 0x1) # reset corr #packet size symSamp = numSamps + prefix_length + postfix_length print("numSamps = %d" % symSamp) print("txSymNum = %d" % txSymNum) upsample = 1 Ts = 1 / rate s_freq = 1e6 s_time_vals = np.array(np.arange(0, numSamps)).transpose() * Ts nb_data = np.exp(s_time_vals * 1j * 2 * np.pi * s_freq).astype( np.complex64) * .25 #create streams rxStreamM = msdr.setupStream(SOAPY_SDR_RX, SOAPY_SDR_CS16, [0, 1]) txStreamM = msdr.setupStream(SOAPY_SDR_TX, SOAPY_SDR_CF32, [0, 1]) if record: rxStreamB = bsdr.setupStream(SOAPY_SDR_RX, SOAPY_SDR_CS16, [0, 1]) # preambles to be sent from BS and correlated against in UE preambles_bs, highest_peak_to_second_ratio_bs = Preambles.getPreambles( 'gold_ifft', 128, 0, upsample=1) #the base station may upsample, but the mobiles won't preambles = preambles_bs[:, :: upsample] #the correlators can run at lower rates, so we only need the downsampled signal. beacon = preambles[0, :] * .25 coe = cfloat2uint32( np.conj(beacon), order='QI') # FPGA correlator takes coefficients in QI order ltsSym = lts.genLTS(upsample=1, cp=0) 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 = np.tile(ltsSym, numSamps / len(ltsSym)).astype( np.complex64) * .5 wbz = np.array([0] * (symSamp), np.complex64) wb_pilot1 = np.concatenate([pad1, wb_pilot, pad2]) wb_pilot2 = wbz #wb_pilot1 if both_channels else wbz bcnz = np.array([0] * (symSamp - prefix_length - len(beacon)), np.complex64) beacon1 = np.concatenate([pad1, beacon, bcnz]) beacon2 = wbz #beacon1 if both_channels else wbz bsched = "PGR" + ''.join("G" * (numSyms - txSymNum - 4)) + ''.join( "R" * txSymNum) + "G" msched = "GGP" + ''.join("G" * (numSyms - txSymNum - 4)) + ''.join( "T" * txSymNum) + "G" if both_channels: bsched = "PGRR" + ''.join("G" * (numSyms - txSymNum - 5)) + ''.join( "R" * txSymNum) + "G" msched = "GGPP" + ''.join("G" * (numSyms - txSymNum - 5)) + ''.join( "T" * txSymNum) + "G" print("Node 1 schedule %s " % bsched) print("Node 2 schedule %s " % msched) bconf = { "tdd_enabled": True, "trigger_out": False, "symbol_size": symSamp, "frames": [bsched] } mconf = { "tdd_enabled": True, "trigger_out": not use_trig, "wait_trigger": wait_trigger, "dual_pilot": both_channels, "symbol_size": symSamp, "frames": [msched] } #mconf = {"tdd_enabled": True, "trigger_out": not use_trig, "wait_trigger": True, "symbol_size" : symSamp, "frames": [msched]} bsdr.writeSetting("TDD_CONFIG", json.dumps(bconf)) msdr.writeSetting("TDD_CONFIG", json.dumps(mconf)) for sdr in [bsdr, msdr]: sdr.writeSetting("TX_SW_DELAY", str(30)) if not use_trig: msdr.writeRegister("IRIS30", CORR_CONF, int( "00004001", 16)) # enable the correlator, with zeros as inputs for i in range(128): msdr.writeRegister("ARGCOE", i * 4, 0) time.sleep(0.1) msdr.writeRegister("ARGCOR", CORR_THRESHOLD, int(threshold)) msdr.writeRegister("ARGCOR", CORR_RST, 0x1) # reset corr msdr.writeRegister("ARGCOR", CORR_RST, 0x0) # unrst corr for i in range(128): msdr.writeRegister("ARGCOE", i * 4, int(coe[i])) if auto_tx_gain: max_gain = int(txgain) min_gain = max(0, max_gain - 15) gain_reg = 0xF000 | (max_gain & 0x3F) << 6 | (min_gain & 0x3F) print("gain reg 0x%X" % gain_reg) msdr.writeRegister( "IRIS30", TX_GAIN_CTRL, gain_reg ) # [15] en, [14] mode, [13:12] step, [11:6] stop, [5:0] start # DEV: ueTrigTime = 153 (prefix_length=0), CBRS: ueTrigTime = 235 (prefix_length=82), tx_advance=prefix_length, corr delay is 17 cycles ueTrigTime = prefix_length + len( beacon) + postfix_length + 17 + tx_advance sf_start = ueTrigTime / symSamp sp_start = ueTrigTime % symSamp print("UE starting symbol and sample count (%d, %d)" % (sf_start, sp_start)) msdr.setHardwareTime( SoapySDR.ticksToTimeNs((sf_start << 16) | sp_start, rate), "TRIGGER" ) # make sure to set this after TDD mode is enabled "writeSetting("TDD_CONFIG", ..." msdr.writeSetting("TDD_MODE", "true") bsdr.writeSetting("TDD_MODE", "true") replay_addr = 0 bsdr.writeRegisters("TX_RAM_A", replay_addr, cfloat2uint32(beacon1, order='IQ').tolist()) bsdr.writeRegisters("TX_RAM_B", replay_addr, cfloat2uint32(beacon2, order='IQ').tolist()) msdr.writeRegisters("TX_RAM_A", replay_addr, cfloat2uint32(wb_pilot1, order='IQ').tolist()) msdr.writeRegisters("TX_RAM_B", replay_addr, cfloat2uint32(wbz, order='IQ').tolist()) if both_channels: msdr.writeRegisters("TX_RAM_A", replay_addr + 2048, cfloat2uint32(wbz, order='IQ').tolist()) msdr.writeRegisters("TX_RAM_B", replay_addr + 2048, cfloat2uint32(wb_pilot2, order='IQ').tolist()) if not use_trig: msdr.writeRegister("IRIS30", CORR_CONF, int( "00004011", 16)) # enable the correlator, with inputs from adc signal.signal(signal.SIGINT, partial(signal_handler, rate, numSyms)) bsdr.writeSetting("TRIGGER_GEN", "") txth = threading.Thread(target=tx_thread, args=(msdr, rate, txStreamM, rxStreamM, nb_data, symSamp, numSyms, txSymNum, numSyms - txSymNum - 1)) txth.start() if record: rxth = threading.Thread(target=rx_thread, args=(bsdr, rxStreamB, symSamp, txSymNum, both_channels)) rxth.start() signal.pause()
def __init__(self, args, rate, freq=None, bw=None, txGain=None, rxGain=None, rxAnt=None, txAnt=None, serials=None, num_samps=None, LTSMode=False, ShowConst=False, ): self.sdrs = [SoapySDR.Device(dict(driver="iris", serial = serial)) for serial in serials] #for serial in serials: # print(serial) # SoapySDR.Device(dict(driver="iris", serial = serial)) #split array in half for normal mode, use last SDR for tx if LTSMode if LTSMode: self.tx_sdrs = [self.sdrs[-1]] self.rx_sdrs = self.sdrs[:-1] else: self.tx_sdrs = self.sdrs[0:len(self.sdrs)//2] self.rx_sdrs = self.sdrs[len(self.sdrs)//2:] self.trig_sdr = self.sdrs[0] self.num_samps = num_samps self.LTSMode = LTSMode self.ShowConst = ShowConst print("Using %i tx Irises and %i rx Irises." % (len(self.tx_sdrs), len(self.rx_sdrs)) ) #initialize in parallel self.sdrs = SoapySDR.Device([dict(driver='iris',serial=s) for s in serials]) threads = [threading.Thread(target=self.initSDR, args=[sdr, rate, freq, bw, txGain, rxGain, rxAnt, txAnt]) for sdr in self.sdrs] for t in threads: t.start() for t in threads: t.join() #Sync timestamps with trigger self.trig_sdr.writeSetting('SYNC_DELAYS', "") for sdr in self.sdrs: sdr.setHardwareTime(0, "TRIGGER") #create rx streams self.rxStreams = [sdr.setupStream(SOAPY_SDR_RX, SOAPY_SDR_CF32, [0, 1], {"remote:prot":"tcp", "remote:mtu":"1024"}) for sdr in self.rx_sdrs] num_rx_r = len(self.rx_sdrs)*2 self.sampsRecv = [np.empty(num_samps+int(num_samps*self.LTSMode*1.5)).astype(np.complex64) for r in range(num_rx_r)] print("Receiving chunks of %i" % len(self.sampsRecv[0])) #create tx stream self.txStreams = [] for sdr in self.tx_sdrs: if not self.LTSMode: txStream = sdr.setupStream(SOAPY_SDR_TX, SOAPY_SDR_CF32, [0, 1], {}) sdr.activateStream(txStream) self.txStreams.append(txStream) print("Activate Tx Stream, replay = " + str(self.LTSMode)) #create our own sinusoid Ts = 1/rate s_length = 768 s_freq = 1e6 s_time_vals = np.array(np.arange(0,s_length)).transpose()*Ts s = np.exp(s_time_vals*1j*2*np.pi*s_freq).astype(np.complex64)*.25 num_tx_r = len(self.tx_sdrs)*2 self.sampsToSend = [np.zeros(num_samps).astype(np.complex64) for r in range(num_tx_r)] if LTSMode: if self.ShowConst: num_syms = 22 num_const = 52*num_syms syms = np.random.randint(2, size=num_const)*2-1 + np.random.randint(2, size=num_const)*2j-1j syms = np.reshape(syms,(num_syms,52)) syms_z = np.append(np.insert(np.insert(syms,0,np.zeros((6,num_syms)),axis=1),32,np.zeros((1,num_syms)),axis=1),np.zeros((num_syms,5)),axis=1) syms_time = np.fft.ifft(np.fft.ifftshift(syms_z,axes=(1)),axis=1) syms_time_cp = np.insert(syms_time,0,np.transpose(syms_time[:,-16:]),axis=1) samps = syms_time_cp.flatten() lts_samps = lts.genLTS() self.sampsToSend = [[], []] self.sampsToSend[0] = np.concatenate((lts_samps,samps,np.zeros(256))).astype(np.complex64) self.sampsToSend[1] = np.concatenate((lts_samps,samps,np.zeros(256))).astype(np.complex64) else: l = np.concatenate((lts.genLTS(),np.zeros(64),s)) self.sampsToSend[0][:len(l)] = l self.sampsToSend[1][len(l)+64:len(l)+len(s)+64] = s usetrig = False for r,sdr in enumerate(self.tx_sdrs): replay_addr = 0 max_replay = 4096 #todo: read from hardware if(len(self.sampsToSend[0]) > max_replay): print("Warning: Continuous mode signal must be less than %d samples. Using first %d samples." % (max_replay, max_replay) ) self.sampsToSend[0] = self.sampsToSend[0][:max_replay] self.sampsToSend[1] = self.sampsToSend[1][:max_replay] sdr.writeRegisters('TX_RAM_A', replay_addr, cfloat2uint32(self.sampsToSend[0]).tolist()) sdr.writeRegisters('TX_RAM_B', replay_addr, cfloat2uint32(self.sampsToSend[1]).tolist()) sdr.writeSetting("TX_REPLAY", str(len(self.sampsToSend[0]))) if False: timeNowNs = sdr.getHardwareTime() txTime = timeNowNs + int(1e8)# 100 ms in the future flags = SOAPY_SDR_WAIT_TRIGGER | SOAPY_SDR_END_BURST if usetrig else SOAPY_SDR_END_BURST | SOAPY_SDR_HAS_TIME txStream = self.txStreams[r] sdr.activateStream(txStream) numSent = 0 while numSent < len(self.sampsToSend[0]): if usetrig: sr = sdr.writeStream(txStream, [self.sampsToSend[r*2][numSent:], self.sampsToSend[r*2+1][numSent:]], len(self.sampsToSend[0])-numSent, flags) else: sr = sdr.writeStream(txStream, [self.sampsToSend[r*2][numSent:], self.sampsToSend[r*2+1][numSent:]], len(self.sampsToSend[0])-numSent, flags, timeNs=txTime) flags = SOAPY_SDR_END_BURST #clear has time for next call print(sr) #assertGreater(sr.ret, 0) numSent += sr.ret if sr.ret == -1: print('Bad Write!') if usetrig: time.sleep(0.1) #seems that the tx data may not have gone through both IP stacks and DMA yet, so we need to wait a bit to avoid a race. sdr.writeSetting("TRIGGER_GEN", "") # GM (not lts mode) else: #scaler = (np.arange(s_length/2)).astype(np.complex64)/(s_length/2) #scaler_tri = np.concatenate((scaler,scaler[::-1])) #s = np.multiply(s,scaler_tri) g_length = 256 #guard length #samples to send is a two channel array of complex floats for r in range(num_tx_r): self.sampsToSend[r][r*(s_length+g_length):(r+1)*(s_length+g_length)-g_length] = s #staggered sinusoids print("Done initializing MIMOGui.")
def update(self): '''Get new samples (from mimo_sdr) and plot them.''' sps = 80 num_syms = 24 spp = 1920//sps fft_len = int(sps*.8) #delay = int(sps*.1) delay = 16 leadtime = 128 samps = self.mimo_sdr.getSamples() if samps == -1: return #don't die -- just try again on the next update if self.LTSMode: (lts_start, ltss, peaks) = lts.findLTS(samps[0][leadtime*2:2048+leadtime*2]) packet_start = lts_start - 32 + leadtime*2 #(sps-fft_len) packet_start = 0 if packet_start < 0 else packet_start #print((lts_start,ltss)) for plt in range(self.num_plots): #if we're in LTSMode we align the samples by detecting the LTS if self.LTSMode: packet_samps = samps[0][packet_start:packet_start+num_syms*sps] #print((packet_start,packet_start+num_syms*sps)) spec = np.log10(np.fft.fftshift(np.square(np.fft.fft(samps[0][:128]))))*10 if False: #highlight packet, but don't adjust viewing window. corr1 = np.correlate(samps[plt],lts.genLTS()[32:64+32])/12 corr2 = corr1[:-64]*corr1[64:] self.Corr_plots[plt].setData(np.abs(corr2[:self.num_samps])) self.I_plots[plt].setData(samps[plt][128:128+self.num_samps].real) # - np.mean(samps[plt][:self.num_samps].real)) self.Q_plots[plt].setData(samps[plt][128:128+self.num_samps].imag) # - np.mean(samps[plt][:self.num_samps].imag)) #self.LR[plt].setRegion([packet_start-128,packet_start+num_syms*sps-128]) #if we plot raw samps, this will track self.LR[plt].setRegion([packet_start-128,packet_start+160+768+64-128]) #if we plot raw samps, this will track else: packet_samps = samps[plt][packet_start-leadtime*2:packet_start+self.num_samps-leadtime] corr1 = np.correlate(packet_samps,lts.genLTS()[32:64+32])/10 corr2 = corr1[:-64]*corr1[64:] packet_samps = packet_samps[leadtime:] #realign correlation self.Corr_plots[plt].setData(np.abs(corr2)) self.I_plots[plt].setData(packet_samps.real) # - np.mean(samps[plt][:self.num_samps].real)) self.Q_plots[plt].setData(packet_samps.imag) # - np.mean(samps[plt][:self.num_samps].imag)) if self.ShowConst: syms = np.reshape(packet_samps, (num_syms,sps) ) #syms_freq = np.fft.fft(np.fft.fftshift(syms[:,delay:delay+fft_len],axes=(1)),axis=1) syms_freq = np.fft.fftshift(np.fft.fft(syms[2:,delay:delay+fft_len],axis=1),axes=(1)) sl = list(range(6,32))+list(range(33,59)) #chan_est = 1/(np.mean(syms_freq[:2,:],axis=0)*lts.lts_freq)[sl] chan_est = 1/(np.mean(np.fft.fftshift(np.fft.fft(np.reshape(packet_samps[16+delay:16+delay+fft_len*2], (2,fft_len) ),axis=1),axes=(1)),axis=0)*lts.lts_freq) chan_est = chan_est[sl] syms_freq = syms_freq[:,sl] syms_freq_eq = syms_freq*chan_est #print(np.abs(chan_est)) syms_freq_flat = syms_freq_eq.flatten() #self.LR[plt].setRegion([0,num_syms*sps]) self.Const_data[plt].setPoints(x=syms_freq_flat.real, y=syms_freq_flat.imag) #let's calculate some power figures for other purposes (e.g. antenna testing) if True: amp = np.mean(np.abs(packet_samps)) pwr = amp**2 print("start: %i end: %i amp: %f pwr: %f pwr_dB: %f" % (packet_start, packet_start+num_syms*sps, amp, pwr, 10*np.log10(pwr)) ) #if we're not in LTSMode, the triggers align everything, so we can just display it. else: self.I_plots[plt].setData(samps[plt][:self.num_samps].real) # - np.mean(samps[plt][:self.num_samps].real)) self.Q_plots[plt].setData(samps[plt][:self.num_samps].imag) # - np.mean(samps[plt][:self.num_samps].imag))