def apply_rate(cur_time): global currRate, npkts, nsuccess, NBYTES, NRETRIES remove_stale_results(cur_time) #"Increment the number of packets sent over the link" npkts += 1 #"If no packets have been successfully acknowledged, return the # highest bit-rate that has not had 4 successive failures." if nsuccess == 0: for i, r in sorted(RATES.items(), reverse=True): if r.succFails < MAXFAILS: currRate = r.rate return [(ieee80211_to_idx(currRate), NRETRIES)] # Every 10 packets, select a random non-failing bit rate w/ better avg tx #"If the number of packets sent over the link is a multiple of ten," if (nsuccess != 0) and (npkts%10 == 0): #"select a random bit-rate from the bit-rates" cavgTX = RATES[currRate].avgTX #" that have not failed four successive times and that #have a minimum packet transmission time lower than the #current bit-rate’s average transmission time." eligible = [r for i, r in RATES.items() if r.losslessTX < cavgTX and r.succFails < MAXFAILS] if len(eligible) > 0: sampleRate = choice(eligible).rate #select random rate from eligible return [(ieee80211_to_idx(sampleRate), NRETRIES)] #"Otherwise, send packet at the bit-rate that has the lowest avg transmission time" # Trusts that currRate is properly maintained to be lowest avgTX return [(ieee80211_to_idx(currRate), NRETRIES)]
def tx_time(mbps, length=1200): #rix is index to RATES, length in bytes ''' Adapted from 802.11 util.c ieee80211_frame_duration() calculate duration (in microseconds, rounded up to next higher integer if it includes a fractional microsecond) to send frame of len bytes (does not include FCS) at the given rate. Duration will also include SIFS. ''' rateinfo = rates.RATES[rates.ieee80211_to_idx(mbps)] if rateinfo.phy == "ofdm": ''' OFDM: N_DBPS = DATARATE x 4 N_SYM = Ceiling((16+8xLENGTH+6) / N_DBPS) (16 = SIGNAL time, 6 = tail bits) TXTIME = T_PREAMBLE + T_SIGNAL + T_SYM x N_SYM + Signal Ext T_SYM = 4 usec 802.11a - 17.5.2: aSIFSTime = 16 usec 802.11g - 19.8.4: aSIFSTime = 10 usec + signal ext = 6 usec ''' dur = 16 # SIFS + signal ext */ dur += 16 # 17.3.2.3: T_PREAMBLE = 16 usec */ dur += 4 # 17.3.2.3: T_SIGNAL = 4 usec */ dur += 4 * (math.ceil((16+8*(length+4)+6)/(4*mbps))+1) # T_SYM x N_SYM else: ''' 802.11b or 802.11g with 802.11b compatibility: 18.3.4: TXTIME = PreambleLength + PLCPHeaderTime + Ceiling(((LENGTH+PBCC)x8)/DATARATE). PBCC=0. 802.11 (DS): 15.3.3, 802.11b: 18.3.4 aSIFSTime = 10 usec aPreambleLength = 144 usec or 72 usec with short preamble aPLCPHeaderLength = 48 usec or 24 usec with short preamble ''' dur = 10 # aSIFSTime = 10 usec dur += (72 + 24) #using short preamble, otw we'd use (144 + 48) dur += math.ceil((8*(length + 4))/mbps)+1 return dur
def apply_rate(cur_time): global packet_count, sample_count, sample_deferred, time_last_called if cur_time - time_last_called >= 1e8: update_stats(cur_time) time_last_called = cur_time #"Minstrel spends a particular percentage of frames, doing "look # around" i.e. randomly trying other rates, to gather # statistics. The percentage of "look around" frames defaults to # 10%. The distribution of lookaround frames is also randomized # somewhat to avoid any potential "strobing" of lookaround between # similar nodes." # Try | Lookaround rate | Normal rate # | random < best | random > best | # -------------------------------------------------------------- # 1 | Best throughput | Random rate | Best throughput # 2 | Random rate | Best throughput | Next best throughput # 3 | Best probability | Best probability | Best probability # 4 | Lowest Baserate | Lowest baserate | Lowest baserate delta = (packet_count * SAMPLING_RATIO // 100) - \ (sample_count + sample_deferred // 2) if delta > 0: # In this case we attempt to sample a rate #"Analysis of information showed that the system was sampling # too hard at some rates. For those rates that never work # (54mb, 500m range) there is no point in sending 10 sample # packets (< 6 ms time). Consequently, for the very very low # probability rates, we sample at most twice." if packet_count >= 10000: sample_count = 0 packet_count = 0 sample_deferred = 0 elif delta > len(RATES) * 2: #"With multi-rate retry, not every planned sample # attempt actually gets used, due to the way the retry # chain is set up - [max_tp,sample,prob,lowest] for # sample_rate < max_tp. # # If there's too much sampling backlog and the link # starts getting worse, minstrel would start bursting # out lots of sampling frames, which would result # in a large throughput loss." sample_count += delta - (len(RATES)*2) # The kernel actually doesn't use a random number generator # here, instead using a pregenerated table of "random" numbers. # See net/mac80211/rc80211_minstrel.c :: init_sample_table # TODO: Use the mechanism the kernel uses randrate = random.choice(list(RATES.keys())) #"Decide if direct ( 1st mrr stage) or indirect (2nd mrr # stage) rate sampling method should be used. Respect such # rates that are not sampled for 20 interations." if randrate < choices.best and RATES[randrate].sample_skipped < 20: #"Only use IEEE80211_TX_CTL_RATE_CTRL_PROBE to mark # packets that have the sampling rate deferred to the # second MRR stage. Increase the sample counter only if # the deferred sample rate was actually used. Use the # sample_deferred counter to make sure that the sampling # is not done in large bursts" probe_flag = True sample_deferred += 1 chain = [choices.best, randrate, choices.prob, choices.base] else: if RATES[randrate].sample_limit != 0: sample_count += 1 if RATES[randrate].sample_limit > 0: RATES[randrate].sample_limit -= 1 chain = [randrate, choices.best, choices.prob, choices.base] else: chain = [choices.best, choices.next, choices.prob, choices.base] mrr = [(ieee80211_to_idx(rate), RATES[rate].adjusted_retry_count) for rate in chain] return mrr
def apply_rate(cur_time): #cur_time is in nanoseconds global npkts, nsuccess, nlookaround, NBYTES, currRate, NRETRIES global bestThruput, nextThruput, bestProb, lowestRate, time_last_called if cur_time - time_last_called >= 1e8: update_stats(cur_time) time_last_called = cur_time #Minstrel spends a particular percentage of frames, doing "look around" i.e. #randomly trying other rates, to gather statistics. The percentage of #"look around" frames defaults to 10%. The distribution of lookaround frames is #also randomized somewhat to avoid any potential "strobing" of lookaround #between similar nodes. #Try | Lookaround rate | Normal rate # | random < best | random > best | #-------------------------------------------------------------- # 1 | Best throughput | Random rate | Best throughput # 2 | Random rate | Best throughput | Next best throughput # 3 | Best probability | Best probability | Best probability # 4 | Lowest Baserate | Lowest baserate | Lowest baserate if RATES[bestThruput].tban > 4: #print("Temp ban on {}, switching to {}!".format(bestThruput, nextThruput)) RATES[bestThruput].tban = 0 bestThruput = nextThruput nextThruput = bestProb if randint(1,100) <= 10 or RATES[bestThruput].tban > 4: #Analysis of information showed that the system was sampling too hard #at some rates. For those rates that never work (54mb, 500m range) #there is no point in sending 10 sample packets (< 6 ms time). Consequently, #for the very very low probability rates, we sample at most twice. random = choice(list(RATES)) while(random == 1): #never sample at lowest rate random = choice(list(RATES)) if random < bestThruput: r = [(ieee80211_to_idx(bestThruput), RATES[bestThruput].adjusted_retry_count), (ieee80211_to_idx(random), RATES[random].adjusted_retry_count), (ieee80211_to_idx(bestProb), RATES[bestProb].adjusted_retry_count), (ieee80211_to_idx(lowestRate), RATES[lowestRate].adjusted_retry_count)] else: #TODO: understand the corresponding kernel code more #and implement if (if necessary) if RATES[random].sample_limit != 0: if RATES[random].sample_limit > 0: RATES[random].sample_limit -= 1 r = [(ieee80211_to_idx(random), RATES[random].adjusted_retry_count), (ieee80211_to_idx(bestThruput), RATES[bestThruput].adjusted_retry_count), (ieee80211_to_idx(bestProb), RATES[bestProb].adjusted_retry_count), (ieee80211_to_idx(lowestRate), RATES[lowestRate].adjusted_retry_count)] else: #normal r = [(ieee80211_to_idx(bestThruput), RATES[bestThruput].adjusted_retry_count), (ieee80211_to_idx(nextThruput), RATES[nextThruput].adjusted_retry_count), (ieee80211_to_idx(bestProb), RATES[bestProb].adjusted_retry_count), (ieee80211_to_idx(lowestRate), RATES[lowestRate].adjusted_retry_count)] return r
def bitrate_type(bitrate): return rates.RATES[ieee80211_to_idx(bitrate)].phy