def calcrxwaveform(self, p, bandwidth=None): """Pre-compute receive waveform and set 'dot11n-rxwaveform'. Use 'dot11n-channel' and 'dot11n-txwaveform' annotations to pre-compute the received waveform (modulo intereference and noise). """ if bandwidth is None: bandwidth = DOT11N_BANDWIDTH # check for annotations for a in ['rxpower', 'dot11n-txwaveform', 'dot11n-channel']: errmsg = "[DOT11N_DSP]: cannot find '%s' annotation!"%(a) assert ANNO.supports(p,a), errmsg A = db2linear(p.getanno('rxpower')) H = p.getanno('dot11n-channel') x = p.getanno('dot11n-txwaveform') # check that channel dimensions match Ntx nrx = H.rows() ntx, hcols = x.rows(), H.cols() assert (hcols == ntx), "[DOT11N]: Got channel with invalid dimensions!" z = sqrt(A)*H.conv(x) # apply local frequency offset (and doppler) if annotations are found cfo = 0 if p.hasanno('tx-cfo') and p.hasanno('rx-cfo'): cfo += p.getanno('tx-cfo') - p.getanno('rx-cfo') if p.hasanno('doppler'): cfo += p.getanno('doppler') z.apply_offset(cfo, bandwidth) # set rxwaveform annotation p.setanno('dot11n-rxwaveform', z) return p
def delrxwaveform(self, p): """Remove waveform annotations from packet `p`.""" wanno = ['dot11n-txwaveform', 'dot11n-rxwaveform', \ 'dot11n-rxnoise', 'dot11n-rxadded', 'dot11n-channel'] for a in [w for w in wanno if ANNO.supports(p,w)]: if p.hasanno(a): p.delanno(a) return p
def sinr_heap(self, p, force=True): """Calculate signal-to-interference-and noise ratio (SINR) for each partition created by `interval_heap()`. :param p: Packet to inspect. :param force: If true, recalculate interval heap, else use existing annotation if it exists. :return: SINR heap. SINR heap has looks like this: [(t0, t1, sinr0), (t1, t2, sinr1), ... ] Note: This method uses the 'rxpower' and 'noisepower' annotations. """ # check packet errmsg = "[CHANNELIF]: sinr_heap() cannot process non-Packet!" assert ANNO.supported(p), errmsg for a in ['rxpower', 'noisepower']: errmsg = "[CHANNELIF]: sinr_heap() cannot find '%s' annotation!"%(a) assert ANNO.supports(p, a), errmsg # get parameters rxpower = p.getanno('rxpower') # in dBm noisepower = p.getanno('noisepower') # in dBm npow = db2linear(noisepower) # get interval heap if p.hasanno('cif-iheap') and not force: iheap = p.getanno('cif-iheap') else: iheap = self.interval_heap(p) # start creating sinr heap sinrheap = [] #print "%s: SINR heap for %s @ %.8f"%(self.traceid, p.traceid, now()) for ta,tb,coll in iheap: ipow = 0 for c in coll: errmsg = "[CHANNELIF]: sinr_heap() cannot find 'rxpower' " + \ "annotation in collision list!" assert ANNO.supports(c, 'rxpower'), errmsg ipow += db2linear(c.getanno('rxpower') ) sinr = rxpower - linear2db(ipow + npow) sinrheap.append((ta,tb,sinr) ) #print " --> (%.8f, %.8f): %.3f dB, coll = %s"%(ta,tb, sinr, [c.traceid for c in coll]) return sinrheap
def cleananno(self, p): """Remove unwanted annotations from packet `p`. Removes any annotations that could cause cyclic references. """ if ANNO.supports(p, 'cif-collision'): coll = strcollision(p) p.delanno('cif-collision') p.setanno('cif-collision', coll, priv=True) return p
def DECODE(self, fsm, pkt): """DECODE state; monitor `radio` and manage packet decoding.""" r = self.radio assert isinstance(r, Radio), "[DOT11A]: Cannot find radio in DECODE!" assert self.isbusy, "[DOT11A]: Carrier sense *not* busy in DECODE!" while fsm.active(): # monitor events and RXD port yield self.RXD.recv(fsm, 1, renege=(r.rxdata, r.rxdone, r.txdata, r.txdone, self.detect)) # receive pkt -> apply error model and forward to upper layers if fsm.acquired(self.RXD): assert len(fsm.got) == 1, ( "[DOT11A]: Received unexpected " + "number of packets from 'RXD' port in DECODE state!" ) p = fsm.got[0] if p is pkt: payload = self.decode(p) if payload: self.log_recv(p) self.cleananno(p) # replace/remove unwanted annotations p.remove_payload() yield self.TXU.send(fsm, [payload]) yield hold, fsm, const.EPSILON # pause before resuming pkt = None # rxdone received before RXD -> interface dropped packet? if r.rxdone in fsm.eventsFired: p = r.rxdone.signalparam if p is pkt: qlen = self.RXD.length drop = ANNO.supports(p, "cif-drp") and p.getanno("cif-drp") errmsg = "[DOT11A]: Unexpected rxdone received in DECODE!" assert (qlen == 0) and drop, errmsg self.log_drop(p, drop="interface dropped packet") pkt = None # rxdata -> drop new packets if r.rxdata in fsm.eventsFired: p = r.rxdata.signalparam assert p is not pkt, "[DOT11A]: Unexpected rxdata for pkt in DECODE!" self.log_drop(p, drop="ignore rxdata in DECODE") # detect -> drop detected packets if self.detect in fsm.eventsFired: p = r.rxdata.signalparam assert p is not pkt, "[DOT11A]: Unexpected detect for pkt in DECODE!" self.log_drop(p, drop="ignore detect in DECODE", decode=pkt.traceid) # ignore otherwise ignore = r.txdata in fsm.eventsFired ignore = ignore or (r.txdone in fsm.eventsFired) if ignore: pass # check if DECODE is done if pkt is None: yield fsm.goto(self.LISTEN) return
def _apply_fixed_delay(cm, *args, **kwargs): """Internal method to implement `FixedDelay` `ChannelModel` wrapper.""" assert isinstance(cm, ChannelModel) assert hasattr(cm, '__subclass__') assert hasattr(cm, '__fixed_delay__') subclass = cm.__subclass__ delay = cm.__fixed_delay__ r = subclass.apply(cm, *args, **kwargs) if ANNO.supports(r, 'cm-delay'): r.delanno('cm-delay') if ANNO.supported(r) and (delay>0): r.setanno('cm-delay', delay) return r
def set_recvanno(self, p): """Overloaded to modify noisepower with noise figure. :return: Modified packet `p`. This method adds the `DOT11A_NOISEFIGURE` to the noisepower specified by `Radio`. """ r = Radio.set_recvanno(self, p) if ANNO.supports(r, "noisepower"): noisepower = r.getanno("noisepower") noisepower += DOT11A_NOISEFIGURE r.setanno("noisepower", noisepower) return r
def strcollision(p): """Get string representation of 'cif-collision' list from packet `p`.""" coll = None if ANNO.supports(p, 'cif-collision'): coll = [] for c in p.getanno('cif-collision'): try: s = c.traceid assert ANNO.supported(c), "s = %s, coll = %s"%(s, coll) except: s = c assert not ANNO.supported(c), "s = %s, coll = %s"%(s, coll) coll.append(s) return coll
def LISTEN(self, fsm, cif, rxport): """Listen to traffic sent by `ChannelInterface` on `rxport`.""" assert (self.hasport(rxport) is not None), \ "[CHANNEL]: Cannot listen on invalid Port!" # get data from rxport yield rxport.recv(fsm, 1) assert fsm.acquired(rxport) and (len(fsm.got)==1), \ "[CHANNEL]: Error occurred during LISTEN on %s!"%(rxport) # forward on all outgoing edge p, u = fsm.got[0], cif neighbors = [str(c.traceid) for c in self.graph.neighbors(u) ] errmsg = "[CHANNEL]: Cannot forward non-Packet!" assert isinstance(p, Packet), errmsg for v in self.graph[u]: cmodel = self.graph[u][v]['model'] errmsg = "[CHANNEL]: Cannot find corresponding TX Port!" assert (self.hasport((v, "TX") ) is not None) # if channel model for link is not active -> ignore packet if not cmodel.active: continue # filter packets using channel model drop = self.apply_filter(cmodel, p, u, v) if drop: self.log("drp",p,drop=drop,src=u.traceid,dst=v.traceid) continue # continue with next link # copy and mark annotations c, txport = p.copy(), self.getport((v, "TX") ) c.setanno('cif-src', u, ref=True) c.setanno('cif-dst', v, ref=True) # apply channel model propdelay = None r = self.apply_model(cmodel, c, u, v) if cmodel.verbose>CHANNEL_VERBOSE: cmodel.log_forward(c, src=u.traceid, dst=v.traceid) if ANNO.supports(r, 'cm-delay'): propdelay = r.getanno('cm-delay') # forward to destination if (r is None): self.log("drp",c,action="dropped by %s"%(cmodel.traceid) ) elif (propdelay>0): # apply propagation delay f = FSM() f.goto(self.FORWARD, txport, [r]) f.start(delay=propdelay) else: # send immediately (no delay) yield txport.send(fsm, [r]) if self.verbose>CHANNEL_VERBOSE: self.log("fwd", p, dest="%s"%(neighbors) ) # continue in LISTEN yield fsm.goto(self.LISTEN, cif, rxport)
def LISTEN(self, fsm): """LISTEN state; monitor `radio` and manage packet detection.""" r = self.radio assert isinstance(r, Radio), "[DOT11A]: Cannot find radio in LISTEN!" while fsm.active(): # check rxenergy -> set csbusy? rxenergy = r.rxenergy() rxhigh = r.inreceive and (r.rxenergy() > DOT11A_CSTHRESHOLD) if rxhigh: self.set_csbusy(rxenergy="high, %.2f dBm" % (rxenergy), rxbuffer=[x.traceid for x in r.rxbuffer]) else: self.set_csidle(rxenergy="%.2f dBm" % (rxenergy)) # monitor events and RXD port yield self.RXD.recv(fsm, 1, renege=(r.rxdata, r.rxdone, r.txdata, r.txdone, self.detect)) # RXD -> ignore incoming packets in LISTEN if fsm.acquired(self.RXD): assert len(fsm.got) == 1, ( "[DOT11A]: Received unexpected " + "number of packets from 'RXD' port in LISTEN state!" ) p = fsm.got[0] self.log_drop(p, drop="not detected in LISTEN") # rxdata -> start DETECT thread if r.rxdata in fsm.eventsFired: p = r.rxdata.signalparam fname = "detect(%s)" % (p._id) ### XXX #### f = FSM() # f = self.newchild(fname, FSM, tracename=fname.upper() ) f.goto(self.DETECT, p) f.start() # detect -> set csbusy -> goto DECODE if self.detect in fsm.eventsFired: p = self.detect.signalparam rxenergy = "%.2f dBm" % (r.rxenergy()) sinr = "%.2f dB" % (self.sinr(p)) self.set_csbusy(p, detect=True, rxenergy=rxenergy) danno = "dot11a-detect" errmsg = "[DOT11A]: Cannot find 'dot11a-detect' " + "annotation in detected packet!" assert ANNO.supports(p, danno) and p.getanno(danno), errmsg # yield hold, fsm, 0 self.log("detect", p, rxenergy=rxenergy, sinr=sinr, rxbuffer=[x.traceid for x in r.rxbuffer]) yield fsm.goto(self.DECODE, p) # ignore otherwise ignore = r.txdata in fsm.eventsFired ignore = ignore or (r.txdone in fsm.eventsFired) ignore = ignore or (r.rxdone in fsm.eventsFired) if ignore: pass return
def rxenergy(self): """Calculate total receive energy of packets in `rxbuffer`. :return: Receive power (in dBm), or -Inf if nothing is in `rxbuffer`. This method uses the 'rxpower' annotation of packets in `rxbuffer` to determine the total receive power. Any packets that do not support this annotation will not contribute to the total power calculated. """ tot = 0 # sum total in milliwatts for p in self.rxbuffer: if ANNO.supports(p, 'rxpower'): prx = p.getanno('rxpower') # rxpower in dBm tot += db2linear(prx) tot_dbm = linear2db(tot) return tot_dbm
def calcper_data(self, p, **kwargs): """Calculate probability of error for data decoding. :param p: Packet being decoded. :param kwargs: Additional keywords arguments passed to `sinr_heap()` (or `sinr()`). :return: PER for decoding packet payload. This method sets the 'dot11a-sinr' and 'dot11a-per' annotations. The operation of this method depends on `DOT11A_USE_PIECEWISE_PER`. """ for a in ["cif-rxts", "cif-duration"]: errmsg = "[DOT11APHY]: calcper_data() cannot find " + "'%s' annotation!" % (a) assert ANNO.supports(p, a), errmsg # verify header parameters plen = len(p.payload) rate, length = p.rate, p.length assert 0 <= rate < len(DOT11A_DATARATE), "[DOT11A]: Invalid rate option (%s)!" % (rate) assert p.length == plen, "[DOT11A]: Header length reported " + "does not equal payload length; %s!=%s" % ( p.length, plen, ) # calculate PER using appropriate method if DOT11A_USE_PIECEWISE_PER: sinrheap = self.sinr_heap(p, **kwargs) t1 = p.getanno("cif-rxts") + p.getanno("cif-duration") t0 = t1 - self.calcnofdm(plen, rate) * DOT11A_TSYM xheap = [(max(ta, t0), min(tb, t1), sinr) for (ta, tb, sinr) in sinrheap if (ta < t1) and (tb > t0)] errmsg = "[DOT11APHY]: Unable to find valid data from SINR heap!" assert len(xheap) > 0, errmsg # calculate piecewise PER and average SINR psuccess, stot = 1.0, 0.0 for ta, tb, sinr in xheap: alpha = (tb - ta) / (t1 - t0) dlen = plen * alpha stot += db2linear(sinr) * alpha psuccess *= 1.0 - self.calcper(dlen, rate, sinr) per = 1.0 - psuccess sinr = linear2db(stot) else: # configure modulation and coding to calculate PER sinr, plen = self.sinr(p, **kwargs), length per = self.calcper(plen, rate, sinr) # set annotations and return PER p.setanno("dot11a-sinr", sinr) p.setanno("dot11a-per", per) return per
def decode_header(self, p, checkcrc=False): """Waveform-level decoding of header. :param p: Packet to decode. :param checkcrc: If true, check header CRC. Set `checkcrc` to false when using `decode_header()` to just do detection. By default, the PHY does detection & decoding in the same functions, so this method is allows for some slight variation in usage. """ # check annotations assert isinstance(p, Dot11N) for a in ['cif-rxts', 'dot11n-rxadded']: errmsg = "[DOT11N]: Cannot find '%s' annotation!"%(a) assert ANNO.supports(p, a), errmsg errmsg = "[DOT11N]: Packet was not added to input!" assert p.hasanno('dot11n-rxadded'), errmsg # get rx input stream y = self.getrxinput(p) detect = self.receiver.decode_header(y, checkcrc) # check header crc? prepad = int(self.MINPAD + DOT11N_TSHORT*DOT11N_BANDWIDTH) startidx = self.receiver.start_index() - prepad crcok = detect # check header parameters plen = len(p.payload) # get header parameters from receiver after decoding rate, length = self.receiver.mcs(), self.receiver.length() okrate = (0<=rate<len(DOT11N_DATARATE) ) oklen = (length == plen) okpar = crcok # calculate start time tstart = p.getanno('cif-rxts') tstart += startidx/DOT11N_BANDWIDTH p.setanno('dot11n-start-index', startidx) p.setanno('dot11n-cfo', self.receiver.get_cfo() ) # return decoding parameters if detect: header = "detect success" else: header = "detect failed" if checkcrc: if crcok: header = "success" else: header = "decoding failed" param = {'status':header, 'detect':detect, 'crcok':crcok, \ 'rate':rate, 'length':length, 'start':tstart} return param
def decode_data(self, p): """Waveform-level decoding of packet payload. :param p: Packet to decode. :return: Decoded data string. """ # check annotations assert isinstance(p, Dot11N) for a in ['cif-rxts', 'dot11n-header', 'dot11n-detect', 'dot11n-rxadded']: errmsg = "[DOT11N]: Cannot find '%s' annotation!"%(a) assert ANNO.supports(p, a), errmsg errmsg = "[DOT11N]: Packet was not added to input!" assert p.hasanno('dot11n-rxadded'), errmsg errmsg = "[DOT11N]: Packet was not detected!" assert p.getanno('dot11n-detect'), errmsg header = p.getanno('dot11n-header') errmsg = "[DOT11N]: Header decoding failed before decode_data()!" assert (header=="success"), errmsg # get parameters to check startidx Ngi = int(DOT11N_TGI*DOT11N_BANDWIDTH) prepad = int(self.MINPAD + DOT11N_TSHORT*DOT11N_BANDWIDTH) startidx = self.receiver.start_index() - prepad ## startidx = -8 # overwrite startidx ## self.receiver.set_start_index(prepad+startidx) # decode payload -> check if startidx is valid y = self.getrxinput(p) data = self.receiver.decode_data(y) # check if decoding error occurred # -> do contents of data match and does CRC pass? crcchk, error = "FAIL", True if (data == str(p.payload)): if not CRC32.haserror(data): crcchk = "OK" error = False # hamming distance between decoded data and packet p hdist = hamming_distance(data, str(p.payload) ) self.log("CRC", p.payload, crcchk=crcchk, error=error, hdist=hdist) # clean up waveform annotations self.delrxwaveform(p) # return decoding parameters param = {'data':data, 'error':error, 'nerror':hdist} # return decoded data return param
def getrxinput(self, p): """Get input to receiver for specified packet. :param p: Packet to grab from input stream. Get the samples corresponding to the input for a given packet. This uses the timestamp annotation 'cif-rxts' and 'cif-duration' annotation to find the appropriate input samples from `rxinput`. """ #self.stdout("%s: called getrxinput() on %s @ %.8f\n"%(self.traceid, p.traceid, now())) errmsg = "[DOT11N]: in getrxinput(), no input stream found!" assert (self.rxinput is not None), errmsg # check annotations for a in ['cif-rxts', 'cif-duration', 'dot11n-rxadded']: errmsg = "[DOT11N]: Cannot find '%s' annotation!"%(a) assert ANNO.supports(p, a), errmsg assert p.getanno('dot11n-rxadded') # calculate start/end of waveform tstart = p.getanno('cif-rxts') duration = p.getanno('cif-duration') tend = tstart + duration # pad returned waveform minpad = self.MINPAD tstart -= minpad/DOT11N_BANDWIDTH tend += minpad/DOT11N_BANDWIDTH # extract from input stream rxts, w = self.rxinput rxend = rxts + w.cols()/DOT11N_BANDWIDTH errmsg = "[DOT11N]: in getrxinput(), packet starts before input " + \ "stream! tpacket, tbuffer = (%.6f, %.6f)"%(tstart, rxts) assert (tstart>=rxts), errmsg # calculare indices istart = int((tstart-rxts)*DOT11N_BANDWIDTH) istop = int((tend-rxts)*DOT11N_BANDWIDTH)-1 assert (istart>-1), "[DOT11N]: In getrxinput(), requested " + \ "packet starting before start of input stream!" assert (istop<=w.cols()), "[DOT11N]: In getrxinput(), requested " + \ "packet beyond end of input stream!" # get rxinput y = w.get_cols(istart, istop) return y
def duration(self, p, *args, **kwargs): """Convenience method to get duration of packet `p`. :return: Duration of packet `p` in seconds. By default, this method calls `PHY.duration()` on the physical layer associated with this `MAC` (i.e. `phy`). If `phy` is not a valid `PHY`, this method will attempt to find and return the 'cif-duration' annotation. Overload this method as needed. :note: If duration cannot be determined, this method may raise an exception. """ if isinstance(self.phy, PHY): return self.phy.duration(p, *args, **kwargs) elif ANNO.supports(p, 'cif-duration'): return p.getanno('cif-duration') else: raise RuntimeError, "[MAC]: Could not determine duration! " + \ "No valid PHY and 'cif-duration' annotation not found!"
def calcper_header(self, p, **kwargs): """Calculate probability of error for header decoding. :param p: Packet being decoded. :param kwargs: Additional keywords arguments passed to `sinr_heap()` (or `sinr()`). :return: PER for header decoding. This method sets the 'dot11a-sinr' and 'dot11a-per' annotations. The operation of this method depends on `DOT11A_USE_PIECEWISE_PER`. """ for a in ["cif-rxts"]: errmsg = "[DOT11APHY]: calcper_header() cannot find " + "'%s' annotation!" % (a) assert ANNO.supports(p, a), errmsg # calculate PER using appropriate method plen = len(p.payload) if DOT11A_USE_PIECEWISE_PER: sinrheap = self.sinr_heap(p, **kwargs) t0 = p.getanno("cif-rxts") + DOT11A_TSHORT + DOT11A_TLONG t1 = t0 + DOT11A_TSIGNAL xheap = [(max(ta, t0), min(tb, t1), sinr) for (ta, tb, sinr) in sinrheap if (ta < t1) and (tb > t0)] errmsg = "[DOT11APHY]: Unable to find valid data from SINR heap!" assert len(xheap) > 0, errmsg # calculate piecewise PER and average SINR psuccess, stot = 1.0, 0.0 for ta, tb, sinr in xheap: alpha = (tb - ta) / (t1 - t0) hlen = len(Dot11A()) * alpha stot += db2linear(sinr) * alpha psuccess *= 1.0 - self.calcper(hlen, 0, sinr) per = 1.0 - psuccess sinr = linear2db(stot) else: sinr, hlen = self.sinr(p, **kwargs), len(p) - plen # configure modulation and coding to calculate PER per = self.calcper(hlen, 0, sinr) # set annotations and return PER p.setanno("dot11a-sinr", sinr) p.setanno("dot11a-per", per) return per
def sinr(self, p, **kwargs): """Calculate signal-to-interference-and-noise ratio (SINR). :param p: Packet to compute SINR for. :param kwargs: Additional keyword arguments passed to `sinr_heap()`. This method uses the 'rxpower', 'noisepower', and 'cif-collision' annotations to calculate the SINR of the received packet p. :note: This method sets the 'phy-sinr' annotation indicating the SINR (in dB). """ for a in ["rxpower", "noisepower", "cif-collision"]: errmsg = "[DOT11APHY]: sinr() cannot find '%s' annotation!" % (a) assert ANNO.supports(p, a), errmsg # get SINR heap sinrheap = self.sinr_heap(p, **kwargs) minsinr = +inf # find minimum SINR for ta, tb, sinr in sinrheap: if sinr < minsinr: minsinr = sinr # set annotations p.setanno("phy-sinr", minsinr) return minsinr
def addrxinput(self, p, **kwargs): """Update local waveform with new packet. :param p: Packet to add to `rxinput`. :param kwargs: Keyword arguments passed to `calcrxwaveform()`. """ # check if waveform has been calculated or already been added to input if p.hasanno('dot11n-rxadded'): return if not p.hasanno('dot11n-rxwaveform'): self.calcrxwaveform(p, **kwargs) # check other annotations for a in ['cif-rxts', 'dot11n-rxwaveform', 'noisepower']: errmsg = "[DOT11N_DSP]: cannot find '%s' annotation!"%(a) assert ANNO.supports(p,a), errmsg # get parameters y = p.getanno('dot11n-rxwaveform') npow = db2linear(p.getanno('noisepower')) nrx = y.rows() yts = p.getanno('cif-rxts') errmsg = "[DOT11N]: Cannot add waveform with %d "%(y.cols() ) + \ "(> %d) samples in addrxinput()!"%(self.MAXINPUT) assert (y.cols()<self.MAXINPUT), errmsg # apply pad to front and back of new input pad = Waveform.zeros(nrx,2*self.MINPAD) y = pad.concat_horizontal(y.concat_horizontal(pad) ) yts -= pad.cols()/DOT11N_BANDWIDTH # initialize rxinput if it is not already set if self.rxinput is None: #self.log("rxadded", p) noise = Waveform.randn(nrx, y.cols(), 0, npow) self.rxinput = (yts, y+noise) p.setanno('dot11n-rxadded', True) return p # update rxinput (rxts, w) = self.rxinput rxend = rxts + w.cols()/DOT11N_BANDWIDTH assert (y.rows() == nrx), "[DOT11N]: Invalid row size in addrxinput()!" assert (w.rows() == nrx), "[DOT11N]: Invalid row size in addrxinput()!" # get indices for begin/end of rxinput and new input rxa, rxb = 0, w.cols()-1 ya = rxa + int((yts-rxts)*DOT11N_BANDWIDTH) yb = ya + y.cols() - 1 istart, istop = min(rxa, ya), max(rxb, yb) errmsg = "[DOT11N]: Cannot use addrxinput() to add " + \ "waveform that starts prior to current input stream!" assert (istart==rxa), errmsg # new waveform starts after current input ends -> reinitialize input if ya>rxb: self.rxinput = None return self.addrxinput(p) # add pads to y as needed if ya>istart: pad = Waveform.zeros(nrx, ya - istart) y = pad.concat_horizontal(y) self.log("-DSP.PAD.IN", p, npad=ya-istart) if yb<istop: pad = Waveform.zeros(nrx, istop - yb) y = y.concat_horizontal(pad) self.log("+DSP.PAD.IN", p, npad=istop-yb) # add pads to (end of) rxinput as needed if rxb<istop: noise = Waveform.randn(nrx, istop-rxb, 0, npow) w = w.concat_horizontal(noise) self.log("+DSP.PAD.RX", p, npad=istop-rxb) # combine waveforms errmsg = "[DOT11N]: Incompatible waveform dimensions, " + \ "y ~ %d x %d, "%(y.rows(),y.cols()) + \ "w ~ %d x %d, "%(w.rows(),w.cols()) + \ "y: [%d, %d], w: [%d, %d]"%(ya,yb,rxa,rxb) assert (y.cols() == w.cols() ), errmsg assert (y.rows() == w.rows() ), errmsg assert (y.cols()>0), errmsg assert (y.rows()>0), errmsg z = y + w # trim input if necessary if z.cols()> self.MAXINPUT: trimidx = z.cols() - self.MAXINPUT z = z.get_cols(trimidx, z.cols()-1) rxts += trimidx/DOT11N_BANDWIDTH self.rxinput = (rxts, z) p.setanno('dot11n-rxadded', True)
def interval_heap(self, p): """Create interval heap from collision list in packet `p`. :param p: Packet containing collision list in 'cif-collision' annotation. :return: Interval heap. This method uses the 'cif-rxts' and 'cif-duration' annotations of packet `p` and each packet in its collision list to create an interval heap. To do this, the method will create a list of partitions over the duration of packet `p` and sort colliding packets into the appropriate partitions. An interval heap looks like: [(t0,t1,[...]), (t1,t2,[...]), ...] This method sets the 'cif-iheap' annotation. """ # get packet p parameters for a in ['cif-collision', 'cif-rxts']: errmsg = "[CHANNELIF]: interval_heap() requires '%s' annotation!"%(a) assert ANNO.supports(p, a), errmsg coll = p.getanno('cif-collision') duration = const.EPSILON if p.hasanno('cif-duration'): duration = p.getanno('cif-duration') ta = p.getanno('cif-rxts') tb = ta + duration # get times for all packets in collision list times = [ta, tb] for c in coll: errmsg = "[CHANNELIF]: interval_heap() requires 'cif-rxts' " + \ "annotation in collision list packet!" assert ANNO.supports(c, 'cif-rxts'), errmsg duration = const.EPSILON if c.hasanno('cif-duration'): duration = c.getanno('cif-duration') t0 = c.getanno('cif-rxts') # start of packet c t1 = t0 + duration # end of packet c # check if t0, t1 are in times t0intimes = any([(abs(t0-t)<2*const.EPSILON) for t in times]) if (not t0intimes) and (ta<t0<tb): times.append(t0) t1intimes = any([(abs(t1-t)<2*const.EPSILON) for t in times]) if (not t1intimes) and (ta<t1<tb): times.append(t1) # sort times and create interval heap times.sort() iheap = [(times[k], times[k+1], []) for k in range(len(times)-1) ] #print "%s: Interval heap for %s (%.8f, %.8f) @ %.8f"%(self.traceid, \ # p.traceid, ta,tb, now()) for c in coll: errmsg = "[CHANNELIF]: interval_heap() requires 'cif-rxts' " + \ "annotation in collision list packet!" assert ANNO.supports(c, 'cif-rxts'), errmsg duration = const.EPSILON if c.hasanno('cif-duration'): duration = c.getanno('cif-duration') t0 = c.getanno('cif-rxts') # start of packet c t1 = t0 + duration # end of packet c # insert into interval heap #print " + inserting %s, (%.8f, %.8f)"%(c.traceid,t0,t1) for k in range(len(iheap)): ia, ib = iheap[k][0], iheap[k][1] errmsg = "[CHANNELIF]: malformed interval in " + \ "interval_heap()! (ia=%s, ib=%s)"%(ia, ib) assert (ia<ib), errmsg if (t0<ib) and (t1>ia): iheap[k][2].append(Reference(c)) #print " --> inserted into (%.8f, %.8f)"%(ia,ib) else: #print " --> not added into (%.8f, %.8f)"%(ia,ib) pass # set iheap annotation p.setanno('cif-iheap', iheap, priv=True) return iheap