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 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 CAPTURE(self, fsm, p): """Simulate capture process for a Packet `p`.""" self.log("CAPSTART", p) errmsg = "[CHANNELIF]: Cannot capture packet that doesn't support ANNO!" assert ANNO.supported(p), errmsg duration = const.EPSILON self.rxbuffer.append(p) # add to rxbuffer if self.intransmit: self.drop("all") # drop all packet in rxbuffer # mark/use other annotations self.set_recvanno(p) if p.hasanno('cif-duration'): duration = p.getanno('cif-duration') assert not(duration<const.EPSILON), \ "[CHANNELIF]: Invlaid duration in CAPTURE! (t=%s)"%(duration) # resume operation self.log("rxdata.sig", p) self.rxdata.signal(p) # signal rxdata yield hold, fsm, duration # simulate duration if self.intransmit: self.drop("all") # drop all packet in rxbuffer self.rxbuffer.remove(p) # remove from rxbuffer # drop or forward to upper layer if p.hasanno('cif-drp') and p.getanno('cif-drp'): self.log_drop( p, halfduplex="%s"%(self.halfduplex) ) self.cleananno(p) else: pargs = {'cif-duration':time2usec(duration) } self.log_recv(p, **pargs) yield self.TXU.send(fsm, [p]) # signal rxdone self.log("rxdone.sig", p) self.rxdone.signal(p) yield fsm.stop() assert False, "stop failed!"
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 SEND(self, fsm): """SEND state; simulate encoding and send process. This state performs the following tasks: 1. Get packet from 'RXU' port. 2. Call `encode()` to generate waveform for outgoing packet. 3. Mark 'cif-duration' annotation with value returned by `duration()`. 4. Simulate `txproctime` for outgoing packet. 5. Send outgoing waveform to 'TXD'. 6. Simulate `duration` of waveform. """ while fsm.active(): yield self.RXU.recv(fsm, 1) assert fsm.acquired(self.RXU) and ( len(fsm.got) == 1 ), "[DOT11APHY]: Error receiving from RXU port in SEND()!" p = fsm.got[0] # simulate encoding and Tx processing time w = self.encode(p) duration = self.duration(p) if ANNO.supported(w): w.setanno("cif-duration", duration) yield hold, fsm, self.txproctime(p) # send waveform and simulate duration self.log_send(w, duration=time2usec(duration)) yield self.TXD.send(fsm, [w]) assert fsm.stored(self.TXD), "[DOT11APHY]: Error sending to TXD in SEND!" yield hold, fsm, duration return
def RECV(self, fsm): """RECV state; monitor traffic from lower layer. This state receives packets on `Port` 'RXD'. It then uses `ptype` to determine which packet handler should process the new data packet, and spawns a new thread to run the handler. :note: Currently, IPv4 is the only protocol type supported. Non-IPv4 packets are passed to `ERRRECV` for handling. """ # error messages rxderror = "[ROUTING]: Error occurred while receiving " + \ "traffic from RXD port!" pkterror = "[ROUTING]: Invalid packet from lower layer!" # get packet from lower layer yield self.RXD.recv(fsm, 1) assert fsm.acquired(self.RXD) and (len(fsm.got)==1), rxderror p = fsm.got[0] if isinstance(p, Reference): p = p._deref assert ANNO.supported(p), pkterror # use appropriate recv function based on ptype if (self.ptype == const.ARP_PTYPE_IP): f = FSM.launch(self.IPRECV, p) # IP ptype else: f = FSM.launch(self.ERRRECV, p) # unknown ptype # continue in RECV yield fsm.goto(self.RECV)
def framedetect(self, p, thresh=None): """Apply packet detection model for detecting training sequence; based on signal-to-interference-and-noise ratio (SINR). :param p: `Dot11A` packet being received. :param thresh: Packet detection SINR threshold (in dB) [default=`DOT11A_FDTHRESHOLD`]. :return: Boolean flag; if true, packet detection was successful. This method checks to make sure `p` is a `Dot11A` packet, and that the received SINR is greater than the receiver detection threshold `thresh`. This method will also mark the 'dot11a-detect' annotation to indicate the success (or failure) of the frame detection. **Overload this method to change how frame detection works.** :note: If `p` is not a `Dot11A` packet, this method will set the 'dot11a-detect' annotation to false and return false. """ if not isinstance(p, Dot11A): if ANNO.supported(p): p.setanno("dot11a-detect", False) return False # check SINR if thresh is None: thresh = DOT11A_FDTHRESHOLD sinr = self.sinr(p) detect = True if sinr < thresh: detect = False # mark annotations p.setanno("dot11a-detect", detect) return detect
def SEND(self, fsm): """Manage downstream (or outgoing traffic.""" yield self.RXU.recv(fsm, 1) assert fsm.acquired(self.RXU) and (len(fsm.got)==1), \ "[CHANNELIF]: SEND() error occurred during recv() from RXU!" # get packet and set annotations p, duration = fsm.got[0], 0 errmsg = "[CHANNELIF]: Cannot send packet that does not support ANNO!" assert ANNO.supported(p), errmsg self.set_sendanno(p) if p.hasanno('cif-duration'): duration = p.getanno('cif-duration') # min-time is const.EPSILON if duration<const.EPSILON: duration = const.EPSILON p.setanno('cif-duration', duration) self.log_send(p) # send and simulate duration self.__ifstate = CHANNELIF_TX # start TX self.drop("all") # drop all packet in rxbuffer self.txdata.signal(p) yield self.TXD.send(fsm, [p]) yield hold, fsm, duration # simulate duration self.drop("all") # drop all packet in rxbuffer self.__ifstate = CHANNELIF_RX # resume RX self.txdone.signal(p) # continue in SEND yield fsm.goto(self.SEND)
def log_recv(self, p, *args, **kwargs): """Updated to print `RAI` parameters.""" if ANNO.supported(p): if p.haslayer(RAI): kwargs['rai-rate'] = p[RAI].rate kwargs['rai-length'] = p[RAI].length if p.hasanno('phy-rate'): kwargs['phy-rate'] = p.getanno('phy-rate') DCF.log_recv(self, p, *args, **kwargs)
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 cleananno(self, p): """Clean up annotations before passing to upper layers.""" assert ANNO.supported(p), "[PHY]: ANNO not supported!" # replace/remove unwanted annotations if p.hasanno('cif-collision'): coll = strcollision(p) assert (coll is not None) 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 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 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 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 set_recvanno(self, p, src, dst): """Set relevant annotations for received data to upper layer. :param p: Packet to modify. :param src: Network address of source. :param dst: Network address of destination. :return: Modified packet. This method sets the 'net-src' and 'net-dst' annotations. """ errmsg = "[ROUTING]: Got non-Packet data from upper layer()!" assert ANNO.supported(p), errmsg # set address annotations p.setanno('net-src', src) p.setanno('net-dst', dst) return p
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 set_sendanno(self, p, src, dst): """Set relevant annotations for outgoing packet. :param p: Packet to modify. :param src: Network address of source. :param dst: Network address of destination. :return: Modified packet. This method sets the 'net-src' and 'net-dst' annotations. """ errmsg = "[ROUTING]: Got non-Packet data from upper layer()!" assert ANNO.supported(p), errmsg # set address annotations p.setanno('net-src', src) p.setanno('net-dst', dst) # set annotations used just for logging p.setanno('net-root', str(p.traceid)) return p
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 SEND(self, fsm): """SEND state; monitor traffic from upstream protocol and pass to routing algorithm. This state uses the 'net-src' and 'net-dst' annotations to determine the addresses of the source and destination. If not available, these values default to the local `address` and `broadcast` address. This state then uses `ptype` to determine which packet handler should process the new data packet, and spawns a new thread to run the handler. :note: Currently, IPv4 is the only protocol type supported. Non-IPv4 packets are passed to `ERRSEND` for handling. Also, this state will check for loopback addresses and send them back to the upper layer protocol. """ # error messages rxuerror = "[ROUTING]: Error occurred receiving from RXU port!" pkterror = "[ROUTING]: Invalid packet from upper layer!" # get packet to send from upperlayer yield self.RXU.recv(fsm, 1) assert fsm.acquired(self.RXU) and (len(fsm.got)==1), rxuerror p = fsm.got[0] if isinstance(p, Reference): p = p._deref assert ANNO.supported(p), pkterror # process new data packet addr = self.address src, dst = addr, self.broadcast if p.hasanno('net-src'): src = p.getanno('net-src') if p.hasanno('net-dst'): dst = p.getanno('net-dst') r = self.set_sendanno(p, src, dst) # send loopback back to upper layer if (dst==addr): self.log_loopback(p, src=src, dst=dst) yield self.TXU.send(fsm, [p]) yield fsm.goto(self.SEND) # continue in SEND # otherwise -> use appropriate send function based on ptype if (self.ptype == const.ARP_PTYPE_IP): f = FSM.launch(self.IPSEND, r, src, dst) # IP ptype else: f = FSM.launch(self.ERRSEND, r, src, dst) # unknown ptype # continue in SEND yield fsm.goto(self.SEND)
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 seterror(self, p): """Convenience method to set error annotation (or parameters) in `p`. :return: Modified packet with updated parameters/annotations. By default, if packet `p` has a `CRC32` layer, this method will set the 'crcerror' field, otherwise this method will set the 'crcerror' annotation to 1. **Overload this method as needed.** """ hascrc = CRC32.supported(p) hasanno = ANNO.supported(p) if hascrc: p[CRC32].crcerror = 1 elif hasanno: p.setanno('crcerror', 1) else: raise RuntimeError, "[PHY]: seterror() failed to find packet!"
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 haserror(self, p, *args, **kwargs): """Convenience method to determine if packet `p` has an error. :param args: Additional arguments passed to `CRC32.haserror()`. :param kwargs: Keyword arguments passed to `CRC32.haserror()`. :return: Boolean; true if error is found; false otherwise. By default, this method calls `CRC32.haserror()`. If no `CRC32` layer is found, or no 'crcerror' annotation is found the packet is assumed to be error-free. **Overload this method as needed to change this operation.** :note: This method ignores the CRC when `checkcrc` is set to false. """ hascrc = isinstance(p, Packet) and p.haslayer(CRC32) hasanno = ANNO.supported(p) and p.hasanno('crcerror') crcerror = (hascrc or hasanno) and CRC32.haserror(p,*args,**kwargs) if not self.checkcrc: crcerror = False # assume no error when checkcrc is false return crcerror
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