def __init__(self): hotspots = load_hotspots() self.h_by_addr = dict() for h in hotspots: self.h_by_addr[h['address']] = h interactives = set([]) found_witness = False try: with open('witnesses.csv', newline='') as csvfile: reader = csv.reader(csvfile, delimiter=',', quotechar='"') count = 0 results = [] for row in reader: dist = haversine_km(self.h_by_addr[row[0].strip()]['lat'], self.h_by_addr[row[0].strip()]['lng'], self.h_by_addr[row[1].strip()]['lat'], self.h_by_addr[row[1].strip()]['lng']) if dist < 0.3: continue interactives.add(row[0].strip()) #interactives.add(row[1].strip()) found_witness = True except FileNotFoundError as e: pass try: with open('witnesses2.csv', newline='') as csvfile: reader = csv.reader(csvfile, delimiter=',', quotechar='"') count = 0 results = [] for row in reader: dist = haversine_km(self.h_by_addr[row[0].strip()]['lat'], self.h_by_addr[row[0].strip()]['lng'], self.h_by_addr[row[1].strip()]['lat'], self.h_by_addr[row[1].strip()]['lng']) if dist < 0.3: continue interactives.add(row[0].strip()) #interactives.add(row[1].strip()) found_witness = True except FileNotFoundError as e: pass if not found_witness: print( "WARNING no 'witnesses.csv' found with addresses of interactive hotsots, will assume all hotspots interactive" ) print( f"\tthis will run to understand the algorithm but will give very wrong results" ) interactives = [h['address'] for h in hotspots] self.interactives = interactives
def summary_table(results, hotspot_transmitting=False): other_pass = 0 other_ttl = 0 other_cnt = 0 all_ttl = 0 all_pass = 0 dist_min = 9999 dist_max = 0 if hotspot_transmitting: print(f"PoC hops from: {hotspot['name']}") print( f"{'to receiving hotspot':30} | owner | {'dist km'} | {'heading'} | recv/ttl | recv % |" ) else: print(f"PoC hops to: {hotspot['name']}") print( f"{'from transmitting hotspot':30} | owner | {'dist km'} | {'heading'} | recv/ttl | recv % |" ) print("-" * 80) for h in results.keys(): ttl = results[h][0] + results[h][1] all_ttl += ttl all_pass += results[h][0] dist, heading = utils.haversine_km(hlat, hlon, H.get_hotspot_by_addr(h)['lat'], H.get_hotspot_by_addr(h)['lng'], return_heading=True) heading = 5 * round(heading / 5, 0) idx = int(round(heading / 45)) % 8 headingstr = ['N', 'NE', 'E', 'SE', 'S', 'SW', 'W', 'NW'] if ttl == 1: other_ttl += ttl other_pass += results[h][0] other_cnt += 1 dist_min = min(dist_min, dist) dist_max = max(dist_max, dist) continue ownr = 'same' if hotspot['owner'] == H.get_hotspot_by_addr( h)['owner'] else H.get_hotspot_by_addr(h)['owner'][-5:] print( f"{H.get_hotspot_by_addr(h)['name']:30} | {ownr:5} | {dist:6.1f} | {heading:4.0f} {headingstr[idx]:>2} | {results[h][0]:3d}/{ttl:3d} | {results[h][0] / ttl * 100:5.0f}% |" ) if other_ttl: print( f"other ({other_cnt:2}){' ' * 20} | N/A | {dist_min:4.1f}-{dist_max:2.0f} | N/A | {other_pass:3d}/{other_ttl:3d} | {other_pass / other_ttl * 100:5.0f}% | " ) print(f"{' ' * 40}{' ' * 10} ---------------------") print( f"{' ' * 40}{' '*10} TOTAL | {all_pass:3d}/{all_ttl:4d} | {all_pass / all_ttl * 100:5.0f}% | " )
def load_wit_file(self, file): with open(file, newline='') as csvfile: reader = csv.reader(csvfile, delimiter=',', quotechar='"') count = 0 results = [] for row in reader: tx = row[0].strip() wit = row[1].strip() if tx not in self.witnesses: self.witnesses[tx] = dict() if wit in self.witnesses[tx]: continue # ignore hotspots that are offline if self.h_by_addr[tx]['status']['online'] != 'online': continue if self.h_by_addr[wit]['status']['online'] != 'online': continue dist = haversine_km(self.h_by_addr[tx]['lat'], self.h_by_addr[tx]['lng'], self.h_by_addr[wit]['lat'], self.h_by_addr[wit]['lng']) if 0.3 < dist < 50: self.witnesses[tx][wit] = dist
def pocv10_violations(hotspot, chals): """ :param hotspot: hotspot object to analyze :param chals: list of challenges :return: """ H = Hotspots() haddr = hotspot['address'] hlat, hlng = hotspot['lat'], hotspot['lng'] transmits_w = dict(total=0, bad_rssi=0, bad_snr=0) receives_w = dict(total=0, bad_rssi=0, bad_snr=0) poc_rcv = dict(total=0, bad_rssi=0, bad_snr=0) bad_neighbors = dict() for chal in chals: transmitter = None for p in chal['path']: if p['challengee'] == haddr: for w in p['witnesses']: dist = utils.haversine_km( hlat, hlng, H.get_hotspot_by_addr(w['gateway'])['lat'], H.get_hotspot_by_addr(w['gateway'])['lng']) if dist < .3: continue rssi_lim = utils.max_rssi(dist) snr_rssi_lim = utils.snr_min_rssi(w['snr']) transmits_w['total'] += 1 if w['gateway'] not in bad_neighbors: bad_neighbors[w['gateway']] = dict(rssi=0, snr=0, ttl=0) bad_neighbors[w['gateway']]['ttl'] += 1 if w['signal'] > rssi_lim: transmits_w['bad_rssi'] += 1 bad_neighbors[w['gateway']]['rssi'] += 1 if w['signal'] < snr_rssi_lim: transmits_w['bad_snr'] += 1 bad_neighbors[w['gateway']]['snr'] += 1 if p['receipt'] and transmitter: dist = utils.haversine_km( hlat, hlng, H.get_hotspot_by_addr(transmitter)['lat'], H.get_hotspot_by_addr(transmitter)['lng'] ) rssi_lim = utils.max_rssi(dist) snr_rssi_lim = utils.snr_min_rssi(p['receipt']['snr']) poc_rcv['total'] += 1 if transmitter not in bad_neighbors: bad_neighbors[transmitter] = dict(rssi=0, snr=0, ttl=0) bad_neighbors[transmitter]['ttl'] += 1 if p['receipt']['signal'] > rssi_lim: poc_rcv['bad_rssi'] += 1 bad_neighbors[transmitter]['rssi'] += 1 if p['receipt']['signal'] < snr_rssi_lim: poc_rcv['bad_snr'] += 1 bad_neighbors[transmitter]['snr'] += 1 else: for w in p['witnesses']: if w['gateway'] != haddr: continue dist = utils.haversine_km( hlat, hlng, H.get_hotspot_by_addr(p['challengee'])['lat'], H.get_hotspot_by_addr(p['challengee'])['lng'] ) if dist < .3: continue rssi_lim = utils.max_rssi(dist) snr_rssi_lim = utils.snr_min_rssi(w['snr']) receives_w['total'] += 1 if p['challengee'] not in bad_neighbors: bad_neighbors[p['challengee']] = dict(rssi=0, snr=0, ttl=0) bad_neighbors[p['challengee']]['ttl'] += 1 if w['signal'] > rssi_lim: receives_w['bad_rssi'] += 1 bad_neighbors[p['challengee']]['rssi'] += 1 if w['signal'] < snr_rssi_lim: receives_w['bad_snr'] += 1 bad_neighbors[p['challengee']]['snr'] += 1 transmitter = p['challengee'] days, remainder = divmod(chals[0]['time'] - chals[-1]['time'], 3600 * 24) hours = int(round(remainder / 3600, 0)) print( f"analyzing {len(chals)} challenges from block {chals[0]['height']}-{chals[-1]['height']} over {days} days, {hours} hrs") print(f"PoC v10 failures for {hotspot['name']}") print(F"SUMMARY") print(f"Category | Total | bad RSSI (%) | bad SNR (%) |") print(f"-----------------------------------------------------------------") print(f"Witnesses to hotspot >300m | {transmits_w['total']:5d} | {transmits_w['bad_rssi']:4d} ({transmits_w['bad_rssi']*100/max(1, transmits_w['total']):3.0f}%) | {transmits_w['bad_snr']:4d} ({transmits_w['bad_snr']*100/max(1, transmits_w['total']):3.0f}%) |") print(f"Hotspot witnessing >300m | {receives_w['total']:5d} | {receives_w['bad_rssi']:4d} ({receives_w['bad_rssi']*100/max(1, receives_w['total']):3.0f}%) | {receives_w['bad_snr']:4d} ({receives_w['bad_snr']*100/max(1, receives_w['total']):3.0f}%) |") print(f"Hotspot PoC receipts | {poc_rcv['total']:5d} | {poc_rcv['bad_rssi']:4d} ({poc_rcv['bad_rssi']*100/max(1, poc_rcv['total']):3.0f}%) | {poc_rcv['bad_snr']:4d} ({poc_rcv['bad_snr']*100/max(1, poc_rcv['total']):3.0f}%) |") print() print() print(f'BY "BAD" NEIGHBOR') print(f"Neighboring Hotspot | dist km | heading | bad RSSI (%) | bad SNR (%) |") print(f"------------------------------+---------+---------+----------------+----------------|") hlat, hlng = hotspot['lat'], hotspot['lng'] for n in bad_neighbors: if bad_neighbors[n]['rssi'] or bad_neighbors[n]['snr']: bad_h = H.get_hotspot_by_addr(n) dist_km, heading = utils.haversine_km( hlat, hlng, bad_h['lat'], bad_h['lng'], return_heading=True ) print(f"{H.get_hotspot_by_addr(n)['name']:29} | {dist_km:5.1f} | {__heading2str__(heading):7} | {bad_neighbors[n]['rssi']:3d}/{bad_neighbors[n]['ttl']:3d} ({bad_neighbors[n]['rssi']*100/bad_neighbors[n]['ttl']:3.0f}%) | {bad_neighbors[n]['snr']:3d}/{bad_neighbors[n]['ttl']:3d} ({bad_neighbors[n]['snr']*100/bad_neighbors[n]['ttl']:3.0f}%) |")
def witness_detail(hotspot, chals, smry_only=False): haddr = hotspot['address'] H = Hotspots() H.update_reference_hspot(address=haddr) hotspot = H.get_hotspot_by_addr(haddr) vars = utils.api_call(path='vars')['data'] print() print(f"Witnesses for: {hotspot['name']}") if not smry_only: print( f"{'time':14} | {'block':7} | {'transmitting hotspot':25} | dist km | valid? | snr | rssi | RUs | inval reason" ) tx_smry = dict() total_RUs = 0 for c in chals: p = c['path'][0] for w in p['witnesses']: if w['gateway'] == haddr: transmitter = H.get_hotspot_by_addr(p['challengee']) # time, transmitter, distance, val/inval, RU, reason inval time_str = dt.datetime.fromtimestamp(w['timestamp'] / 1e9).isoformat()[5:19] time_str = time_str.replace('T', ' ') transmitter_name = transmitter['name'] reward_units = 0 valid = 'INVAL' reason = '' dist_km = utils.haversine_km(transmitter['lat'], transmitter['lng'], hotspot['lat'], hotspot['lng']) max_rssi = utils.max_rssi(dist_km) min_rssi = utils.snr_min_rssi(w['snr']) if w['is_valid']: valid = 'valid' hip15_rus = 1 if len(p['witnesses']) > vars['witness_redundancy']: hip15_rus = (vars['witness_redundancy'] - (1 - pow( vars['poc_reward_decay_rate'], len(p['witnesses']) - vars['witness_redundancy'])) ) / len(p['witnesses']) reward_units = transmitter['reward_scale'] * hip15_rus else: if dist_km < 0.3: reason = 'too close' elif w['signal'] > max_rssi: reason = f'rssi too high ({w["signal"]}dbm,{w["snr"]:.1f}snr)' elif w['signal'] < min_rssi: reason = f'snr too high (snr:{w["snr"]:.1f}, rssi: {w["signal"]}<{min_rssi})' else: reason = 'unknown' total_RUs += reward_units if not smry_only: print( f"{time_str:14} | {c['height']:7} | {transmitter_name[:25]:25} | {dist_km:6.1f} | {valid:6} | {w['snr']:5.1f} | {w['signal']:4d} | {reward_units:4.2f} | {reason}" ) if transmitter['address'] not in tx_smry: tx_smry[transmitter['address']] = dict(valid_cnt=0, invalid_cnt=0, RUs=0) tx_smry[transmitter['address']]['RUs'] += reward_units tx_smry[ transmitter['address']]['valid_cnt'] += valid == 'valid' tx_smry[ transmitter['address']]['invalid_cnt'] += valid != 'valid' if smry_only: idx_sort = [] for k in tx_smry.keys(): idx_sort.append((tx_smry[k]['RUs'], k)) idx_sort.sort() idx_sort = [x[1] for x in idx_sort[::-1]] print( f"{'transmitting hotspot':25} | scale | owner | dist km | heading | valids | invlds | RUs" ) earning_by_compass = dict() for addr in idx_sort: txer = H.get_hotspot_by_addr(addr) dist, heading = utils.haversine_km(hotspot['lat'], hotspot['lng'], txer['lat'], txer['lng'], return_heading=True) compass = utils.heading_to_compass(heading) if compass not in earning_by_compass: earning_by_compass[compass] = 0 earning_by_compass[compass] += tx_smry[addr]['RUs'] heading = round(heading / 5, 0) * 5 owner = 'same' if hotspot['owner'] != txer['owner']: owner = txer['owner'][-5:] heading_str = f"{heading:3.0f} {compass:3}" print( f"{txer['name'][:25]:25} | {txer['reward_scale']:5.2f} | {owner:5} | {dist:7.1f} | {heading_str:7} | {tx_smry[addr]['valid_cnt']:6} | {tx_smry[addr]['invalid_cnt']:6} | {tx_smry[addr]['RUs']:.2f}" ) print() print(f"Earnings by compass heading") print(f"heading | RUs | bar chart") max_compass = max(list(earning_by_compass.values())) if max_compass == 0: max_compass = 1 for h in utils.compass_headings: earnings = earning_by_compass.get(h, 0) print( f" {h:4} | {earnings:6.2f} | {'X' * round(32 * earnings / max_compass) }" ) print(f"total RUs earned: {total_RUs:.4f}") if has_mpl and show_plots: ax = plt.subplot(111, projection='polar') ax.set_theta_offset(math.pi / 2) ax.set_theta_direction(-1) width = 2 * math.pi / len(utils.compass_headings) theta = [ math.radians(utils.compass_to_heading(h)) for h in utils.compass_headings ] earnings = [ math.sqrt(earning_by_compass.get(h, 0)) for h in utils.compass_headings ] # earnings = [(earning_by_compass.get(h, 0)) for h in utils.compass_headings] ax.bar(theta, earnings, width=width, bottom=0.0) ax.set_xticks(theta) ax.set_yticks([]) ax.set_xticklabels(utils.compass_headings) plt.title(f"{hotspot['name']} RUs by compass heading") plt.show()
def transmit_details(hotspot, challenges, smry_only=False): """ Prints a list of all transmits, and number of valid / invalid witnesses :param hotspot: :param challenges: :return: """ results = [] vars = utils.api_call(path='vars').get('data', dict()) haddr = hotspot['address'] H = Hotspots() H.update_reference_hspot(address=haddr) hotspot = H.get_hotspot_by_addr(haddr) print(f"Beacons FROM: {hotspot['name']}") print(f"tx_reward_scale: {hotspot['reward_scale']:.3f}") if not smry_only: print(f"Individual Beacons ==========") print( f"{'Beacon Time':14} | {'block':7} | blck Δ | p2p port | Valid | inval | near | RU's | witness bar chart" ) block_deltas = [] last_block = None total_RUs = 0 by_receiver = dict() for c in challenges: if c['path'][0]['challengee'] != hotspot['address']: continue # I am not transmitter block_delta_str = 'N/A' if last_block: block_delta = last_block - c['height'] block_deltas.append(block_delta) block_delta_str = f"{block_delta}" last_block = c['height'] beacon = dict(date=None, height=c['height'], valid=0, invalid=0, close=0, RUs=0) if c['path'][0]['receipt']: ts = c['path'][0]['receipt']['timestamp'] / 1e9 elif c['path'][0]['witnesses']: # if receipt missing and witnesses use first witness ts ts = c['path'][0]['witnesses'][0]['timestamp'] / 1e9 # print(f"No challengee receipt") else: continue # should be unreachble, cant have a poc_receipt with no witness or challengee receipt beacon['date'] = dt.datetime.fromtimestamp( ts).isoformat()[5:19].replace('T', ' ') for w in c['path'][0]['witnesses']: w_hspot = H.get_hotspot_by_addr(w['gateway']) if not w_hspot: continue by_receiver.setdefault( w['gateway'], dict(dist_km=9999, valid=0, invalid=0, near=0)) dist_km = utils.haversine_km(w_hspot['lat'], w_hspot['lng'], hotspot['lat'], hotspot['lng']) by_receiver[w['gateway']]['dist_km'] = dist_km if w['is_valid']: beacon['valid'] += 1 by_receiver[w['gateway']]['valid'] += 1 else: if dist_km <= .32: beacon['close'] += 1 by_receiver[w['gateway']]['near'] += 1 else: beacon['invalid'] += 1 by_receiver[w['gateway']]['invalid'] += 1 tx = 0 if beacon['valid']: tx, _ = utils.hip15_rewards(beacon['valid'] + beacon['invalid'], vars) tx = tx * beacon['valid'] / ( beacon['valid'] + beacon['invalid']) * hotspot['reward_scale'] beacon['RUs'] = tx results.append(beacon) total_RUs += tx p2p_str = '????' challenger_addrs = H.get_hotspot_by_addr( c['challenger'])['status']['listen_addrs'] if challenger_addrs: if '/p2p-circuit/' in challenger_addrs[0]: p2p_str = 'circuit' elif 'tcp/' in challenger_addrs[0]: p2p_str = challenger_addrs[0].split('/')[-1] if not smry_only: print( f"{beacon['date']} | {beacon['height']:7} | {block_delta_str:6} | {p2p_str:>8} | {beacon['valid']:5} | {beacon['invalid']:5} | {beacon['close']:4} | {beacon['RUs']:4.2f} | {'V' * beacon['valid'] + 'i' * beacon['invalid'] + 'c' * beacon['close']}" ) # + '-' * (25 - beacon['valid'] - beacon['invalid'] - beacon['close'])}") #print(beacon) if (not smry_only) and results: print() print(f'Witness reliability ======') print(f"{'Witness name':25} | dist km | valid (%) | inval (%) ") sorted_keys = [] for k in by_receiver.keys(): sorted_keys.append((by_receiver[k]['valid'], k)) sorted_keys.sort() for item in sorted_keys[::-1]: k = item[1] wit = H.get_hotspot_by_addr(k) print( f"{wit['name'][:25]:25} | {by_receiver[k]['dist_km']:7.2f} | {by_receiver[k]['valid']:2d} ({by_receiver[k]['valid']*100/len(results):3.0f}%) | {by_receiver[k]['invalid']:2d} ({by_receiver[k]['invalid']*100/len(results):3.0f}%) " ) listen_addr = 'NONE' if hotspot['status']['listen_addrs']: listen_addr = hotspot['status']['listen_addrs'][0] print() print(f"summary stats") print(f"hotspot address: {hotspot['address']}") print(f"hotspot listening_addr: {listen_addr}") print( f"blocks between chalng avg: {statistics.mean(block_deltas) if block_deltas else -1:.0f}" ) print( f" median: {statistics.median(block_deltas) if block_deltas else -1:.0f}" ) print( f" 75th-percentile: {statistics.quantiles(block_deltas)[-1] if block_deltas else -1:.0f}" ) print( f" range (min - max): {min(block_deltas) if block_deltas else 0} - {max(block_deltas) if block_deltas else 0}" ) if smry_only: print() print(f" Beacons by Day ==========") print(f"note may be partial first and last day") day_dict = dict() for res in results: date = res['date'][:5] if date not in day_dict: day_dict[date] = dict(count=0, RUs=0, valid=0, invalid=0, close=0) day_dict[date]['count'] += 1 day_dict[date]['RUs'] += res['RUs'] day_dict[date]['valid'] += res['valid'] day_dict[date]['invalid'] += res['invalid'] day_dict[date]['close'] += res['close'] print( f"{'Date':5} | bcns | valid | inval | near | RU's | bcn bar chart" ) for k in day_dict.keys(): print( f"{k:5} | {day_dict[k]['count']:4} | {day_dict[k]['valid']:5} | {day_dict[k]['invalid']:5} | {day_dict[k]['close']:4} | {day_dict[k]['RUs']:5.2f} | {'X' * day_dict[k]['count']} " ) print() block_interval = 3000 print(f"Beacons by {block_interval} blocks ========") print(f"note may be partial last set of blocks") if results: start_block = results[0]['height'] block_dict = dict() for res in results: date = int((start_block - res['height']) / block_interval) if date not in block_dict: block_dict[date] = dict(count=0, RUs=0, valid=0, invalid=0, close=0) block_dict[date]['count'] += 1 block_dict[date]['RUs'] += res['RUs'] block_dict[date]['valid'] += res['valid'] block_dict[date]['invalid'] += res['invalid'] block_dict[date]['close'] += res['close'] print( f"{'Block age':9} | bcns | valid | inval | near | RU's | bcn bar chart" ) for k in block_dict.keys(): block_age = f"{k * block_interval:5}+" print( f"{block_age:>9} | {block_dict[k]['count']:4} | {block_dict[k]['valid']:5} | {block_dict[k]['invalid']:5} | {block_dict[k]['close']:4} | {block_dict[k]['RUs']:5.2f} | {'X' * block_dict[k]['count']} " ) print(f"total RU's earned: {total_RUs:.4f}")
def transmit_details(hotspot, challenges, smry_only=False): """ Prints a list of all transmits, and number of valid / invalid witnesses :param hotspot: :param challenges: :return: """ results = [] vars = utils.api_call(path='vars').get('data', dict()) haddr = hotspot['address'] H = Hotspots() H.update_reference_hspot(address=haddr) hotspot = H.get_hotspot_by_addr(haddr) print(f"Beacons FROM: {hotspot['name']}") if not smry_only: print(f"Individual Beacons ==========") print(f"{'Beacon Time':14} | {'block':7} | blck Δ | Valid | inval | near | RU's | witness bar chart") block_deltas = [] last_block = None for c in challenges: if c['path'][0]['challengee'] != hotspot['address']: continue # I am not transmitter block_delta_str = 'N/A' if last_block: block_delta = last_block - c['height'] block_deltas.append(block_delta) block_delta_str = f"{block_delta}" last_block = c['height'] beacon = dict(date=None, height=c['height'], valid=0, invalid=0, close=0, RUs=0) ts = c['time'] if c['path'][0]['receipt']: ts = c['path'][0]['receipt']['timestamp'] / 1e9 elif c['path'][0]['witnesses']: # if receipt missing and witnesses use first witness ts ts = c['path'][0]['witnesses'][0]['timestamp'] / 1e9 # print(f"No challengee receipt") else: continue # should be unreachble, cant have a poc_receipt with no witness or challengee receipt beacon['date'] = dt.datetime.fromtimestamp(ts).isoformat()[5:19].replace('T', ' ') for w in c['path'][0]['witnesses']: w_hspot = H.get_hotspot_by_addr(w['gateway']) if not w_hspot: continue dist_km = utils.haversine_km(w_hspot['lat'], w_hspot['lng'], hotspot['lat'], hotspot['lng']) if w['is_valid']: beacon['valid'] += 1 else: if dist_km <= .32: beacon['close'] += 1 else: beacon['invalid'] += 1 tx = 0 if beacon['valid']: tx, _ = utils.hip15_rewards(beacon['valid'] + beacon['invalid'], vars) tx = tx * beacon['valid'] / (beacon['valid'] + beacon['invalid']) * hotspot['reward_scale'] beacon['RUs'] = tx results.append(beacon) if not smry_only: print(f"{beacon['date']} | {beacon['height']:7} | {block_delta_str:6} | {beacon['valid']:5} | {beacon['invalid']:5} | {beacon['close']:4} | {beacon['RUs']:4.2f} | {'V' * beacon['valid'] + 'i' * beacon['invalid'] + 'c' * beacon['close']}") # + '-' * (25 - beacon['valid'] - beacon['invalid'] - beacon['close'])}") #print(beacon) print() print(f"summary stats") print(f"challenger address: {hotspot['address']}") print(f"challenger listening_addr: {hotspot['status']['listen_addrs'][0]}") print(f"blocks between chalng avg: {statistics.mean(block_deltas):.0f}") print(f" median: {statistics.median(block_deltas):.0f}") print(f" 75th-percentile: {statistics.quantiles(block_deltas)[-1]:.0f}") print(f" range (min - max): {min(block_deltas)} - {max(block_deltas)}") if smry_only: print() print(f" Beacons by Day ==========") print(f"note may be partial first and last day") day_dict = dict() for res in results: date = res['date'][:5] if date not in day_dict: day_dict[date] = dict(count=0, RUs=0, valid=0, invalid=0, close=0) day_dict[date]['count'] += 1 day_dict[date]['RUs'] += res['RUs'] day_dict[date]['valid'] += res['valid'] day_dict[date]['invalid'] += res['invalid'] day_dict[date]['close'] += res['close'] print(f"{'Date':5} | bcns | valid | inval | near | RU's | bcn bar chart") for k in day_dict.keys(): print(f"{k:5} | {day_dict[k]['count']:4} | {day_dict[k]['valid']:5} | {day_dict[k]['invalid']:5} | {day_dict[k]['close']:4} | {day_dict[k]['RUs']:5.2f} | {'X' * day_dict[k]['count']} ") print() block_interval = 3000 print(f"Beacons by {block_interval} blocks ========") print(f"note may be partial last set of blocks") start_block = results[0]['height'] block_dict = dict() for res in results: date = int((start_block - res['height']) / block_interval) if date not in block_dict: block_dict[date] = dict(count=0, RUs=0, valid=0, invalid=0, close=0) block_dict[date]['count'] += 1 block_dict[date]['RUs'] += res['RUs'] block_dict[date]['valid'] += res['valid'] block_dict[date]['invalid'] += res['invalid'] block_dict[date]['close'] += res['close'] print(f"{'Block age':9} | bcns | valid | inval | near | RU's | bcn bar chart") for k in block_dict.keys(): block_age = f"{k * block_interval:5}+" print( f"{block_age:>9} | {block_dict[k]['count']:4} | {block_dict[k]['valid']:5} | {block_dict[k]['invalid']:5} | {block_dict[k]['close']:4} | {block_dict[k]['RUs']:5.2f} | {'X' * block_dict[k]['count']} ")
def poc_polar(hotspot, chals): H = Hotspots() haddr = hotspot['address'] hlat, hlng = hotspot['lat'], hotspot['lng'] hname = hotspot['name'] if os.path.exists(hname): files = glob(hname + '\\*') for file in files: os.remove(file) else: os.mkdir(hname) wl = {} #witnesslist rl = { } #received list of hotspots(hotspot of intereset has been witness to these or received from them) c = 299792458 for chal in chals: # loop through challenges for p in chal['path']: #path? if p['challengee'] == haddr: # handles cases where hotspot of interest is transmitting for w in p[ 'witnesses']: #loop through witnesses so we can get rssi at each location challenge received #print('witness',w) lat = H.get_hotspot_by_addr(w['gateway'])['lat'] lng = H.get_hotspot_by_addr(w['gateway'])['lng'] name = H.get_hotspot_by_addr(w['gateway'])['name'] dist_km, heading = utils.haversine_km(hlat, hlng, lat, lng, return_heading=True) fspl = 20 * log10((dist_km + 0.01) * 1000) + 20 * log10( 915000000) + 20 * log10(4 * pi / c) - 27 try: wl[w['gateway']]['lat'] = lat wl[w['gateway']]['lng'] = lng wl[w['gateway']]['rssi'].append(w['signal']) except KeyError: wl[w['gateway']] = { 'rssi': [ w['signal'], ], 'dist_km': dist_km, 'heading': heading, 'fspl': fspl, 'lat': lat, 'lng': lng, 'name': name } else: # hotspot of interest is not transmitting but may be a witness challengee = p['challengee'] name = H.get_hotspot_by_addr(challengee)['name'] for w in p['witnesses']: if w['gateway'] != haddr: continue #print('transmitter ', name) #print('witness ', H.get_hotspot_by_addr(w['gateway'])['name']) # hotspot of interest was a witness lat = H.get_hotspot_by_addr(challengee)['lat'] lng = H.get_hotspot_by_addr(challengee)['lng'] #name=H.get_hotspot_by_addr(w['gateway'])['name'] dist_km, heading = utils.haversine_km(hlat, hlng, lat, lng, return_heading=True) fspl = 20 * log10((dist_km + 0.01) * 1000) + 20 * log10( 915000000) + 20 * log10(4 * pi / c) - 27 try: rl[challengee]['lat'] = lat rl[challengee]['lng'] = lng rl[challengee]['rssi'].append(w['signal']) except KeyError: rl[challengee] = { 'rssi': [ w['signal'], ], 'dist_km': dist_km, 'heading': heading, 'fspl': fspl, 'lat': lat, 'lng': lng, 'name': name } #print('rl:',rl) ratios = [1.0] * 16 rratios = [1.0] * 16 N = len(ratios) - 1 angles = [] rangles = [] #angles = [n / float(N) *2 *pi for n in range(N+1)] angles = list(np.arange(0.0, 2 * np.pi + (2 * np.pi / N), 2 * np.pi / N)) rangles = list(np.arange(0.0, 2 * np.pi + (2 * np.pi / N), 2 * np.pi / N)) #print(angles,len(angles)) #print(ratios,len(ratios)) markers = [] encoded = {} rencoded = {} for w in wl: #for witness in witnesslist #print(wl[w]) mean_rssi = sum(wl[w]['rssi']) / len(wl[w]['rssi']) ratio = wl[w]['fspl'] / mean_rssi * (-1) if ratio > 3.0: ratio = 3.0 elif ratio < -3.0: ratio = -3.0 ratios.append(ratio) angles.append(wl[w]['heading'] * pi / 180) #markers.append(folium.Marker([wl[w]['lat'],wl[w]['lng']],popup=wl[w]['name'])) markers.append([[wl[w]['lat'], wl[w]['lng']], wl[w]['name']]) # the histogram of the data #unique=set(wl[w]['rssi']) #num_unique=len(unique) n, bins, patches = plt.hist( wl[w]['rssi'], 10) #, density=True, facecolor='g', alpha=0.75,) plt.xlabel('RSSI(dB)') plt.ylabel('Count(Number of Packets)') wit = str(wl[w]['name']) plt.title('Packets from ' + hname + ' measured at ' + wit) #plt.text(60, .025, r'$\mu=100,\ \sigma=15$') #plt.xlim(40, 160) #plt.ylim(0, 0.03) plt.grid(True) #plt.show() strFile = str(wl[w]['name']) + '.jpg' strWitness = str(wl[w]['name']) if os.path.isfile(strFile): #print('remove') os.remove(strFile) # Opt.: os.system("rm "+strFile) plt.savefig(hname + '//' + strFile) encoded[strWitness] = base64.b64encode( open(hname + '//' + strFile, 'rb').read()) plt.close() for w in rl: #for witness in witnesslist #print(rl[w]) mean_rssi = sum(rl[w]['rssi']) / len(rl[w]['rssi']) rratio = rl[w]['fspl'] / mean_rssi * (-1) if rratio > 3.0: rratio = 3.0 elif rratio < -3.0: rratio = -3.0 rratios.append(rratio) rangles.append(rl[w]['heading'] * pi / 180) #markers.append([[wl[w]['lat'],wl[w]['lng']],wl[w]['name']]) n, bins, patches = plt.hist( rl[w]['rssi'], 10) #, density=True, facecolor='g', alpha=0.75,) plt.xlabel('RSSI(dB)') plt.ylabel('Count(Number of Packets)') wit = str(rl[w]['name']) plt.title('Packets from ' + wit + ' measured at ' + hname) plt.grid(True) #plt.show() strFile = 'rrr' + str(rl[w]['name']) + '.jpg' strWitness = str(rl[w]['name']) if os.path.isfile(strFile): #print('remove') os.remove(strFile) # Opt.: os.system("rm "+strFile) plt.savefig(hname + '//' + strFile) rencoded[strWitness] = base64.b64encode( open(hname + '//' + strFile, 'rb').read()) plt.close() # create polar chart angles, ratios = zip(*sorted(zip(angles, ratios))) rangles, rratios = zip(*sorted(zip(rangles, rratios))) angles, ratios = (list(t) for t in zip(*sorted(zip(angles, ratios)))) rangles, rratios = (list(t) for t in zip(*sorted(zip(rangles, rratios)))) fig, ax = plt.subplots(subplot_kw=dict(projection='polar')) ax.set_theta_zero_location("N") ax.set_theta_direction(-1) #ax.set_rmax(3) #ax.set_rmin(-3) ax.set_ylim(-3, 3) ax.plot(angles, ratios, marker='^', linestyle='solid', color='tomato', linewidth=2, markersize=5, label='Transmitting') #markerfacecolor='m', markeredgecolor='k', ax.plot(rangles, rratios, marker='v', linestyle='solid', color='dodgerblue', linewidth=1, markersize=5, label='Receiving') #, markerfacecolor='m', markeredgecolor='k' ax.legend(bbox_to_anchor=(0, 1), fancybox=True, framealpha=0, loc="lower left", facecolor='#000000') plt.xlabel('FSPL/RSSI') plt.savefig(hname + '//' + hname + '.png', transparent=True) #plt.show() # add polar chart as a custom icon in map m = folium.Map([hlat, hlng], tiles='stamentoner', zoom_start=18, control_scale=True, max_zoom=20) polargroup = folium.FeatureGroup(name='Polar Plot') icon = folium.features.CustomIcon(icon_image=hname + '//' + hotspot['name'] + '.png', icon_size=(640, 480)) marker = folium.Marker([hlat, hlng], popup=hotspot['name'], icon=icon) polargroup.add_child(marker) # add witness markers hsgroup = folium.FeatureGroup(name='Witnesses') hsgroup.add_child(folium.Marker([hlat, hlng], popup=hotspot['name'])) # add the witness markers for marker in markers: #html = '<img src="data:image/jpg;base64,{}">'.format html = '<p><img src="data:image/jpg;base64,{}" alt="" width=640 height=480 /></p> \ <p><img src="data:image/jpg;base64,{}" alt="" width=640 height=480 /></p>'.format #print('marker',marker) try: iframe = IFrame(html(encoded[marker[1]].decode('UTF-8'), rencoded[marker[1]].decode('UTF-8')), width=640 + 25, height=960 + 40) popup = folium.Popup(iframe, max_width=2650) mark = folium.Marker(marker[0], popup=popup) hsgroup.add_child(mark) except KeyError: # this means this witness never heard from us so there is no marker for it pass # not sure where to put the receive packet histogram so just ignore for now radius = 0.01 center = Point(hlat, hlng) circle = center.buffer(radius) # Degrees Radius gjcircle = shapely.geometry.mapping(circle) circle = center.buffer(radius * 25) # Degrees Radius gjcircle8 = shapely.geometry.mapping(circle) dcgroup = folium.FeatureGroup(name='Distance Circles', show=False) radius = 0.01 center = Point(hlat, hlng) circle = center.buffer(radius) # Degrees Radius gjcircle = shapely.geometry.mapping(circle) circle = gjcircle['coordinates'][0] my_Circle = folium.Circle(location=[hlat, hlng], radius=300, popup='300m', tooltip='300m') dcgroup.add_child(my_Circle) my_Circle = folium.Circle(location=[hlat, hlng], radius=1000, popup='1km', tooltip='1km') dcgroup.add_child(my_Circle) my_Circle = folium.Circle(location=[hlat, hlng], radius=2000, popup='2km', tooltip='2km') dcgroup.add_child(my_Circle) my_Circle = folium.Circle(location=[hlat, hlng], radius=3000, name='circles', popup='3km', tooltip='3km') dcgroup.add_child(my_Circle) my_Circle = folium.Circle(location=[hlat, hlng], radius=4000, popup='4km', tooltip='4km') dcgroup.add_child(my_Circle) my_Circle = folium.Circle(location=[hlat, hlng], radius=5000, popup='5km', tooltip='5km') dcgroup.add_child(my_Circle) my_Circle = folium.Circle(location=[hlat, hlng], radius=10000, popup='10km', tooltip='10km') dcgroup.add_child(my_Circle) h3colorgroup = folium.FeatureGroup(name='h3 Hexagon Grid Color Fill', show=False) style = {'fillColor': '#f5f5f5', 'lineColor': '#ffffbf'} #polygon = folium.GeoJson(gjson, style_function = lambda x: style).add_to(m) h3group = folium.FeatureGroup(name='h3 r11 Hex Grid', show=False) h3namegroup = folium.FeatureGroup(name='h3 r11 Hex Grid Names', show=False) h3fillgroup = folium.FeatureGroup(name='h3 r11 Hex Grid Color Fill', show=True) h3r8namegroup = folium.FeatureGroup(name='h3 r8 Hex Grid Names', show=False) h3r8group = folium.FeatureGroup(name='h3 r8 Hex Grid', show=False) hexagons = list(h3.polyfill(gjcircle, 11)) hexagons8 = list(h3.polyfill(gjcircle8, 8)) polylines = [] lat = [] lng = [] i = 0 #print('hexagon',hexagons[0]) #print(dir(h3)) home_hex = h3.geo_to_h3(hlat, hlng, 11) a = h3.k_ring(home_hex, 7) for h in a: gjhex = h3.h3_to_geo_boundary(h, geo_json=True) gjhex = geometry.Polygon(gjhex) mean_rsrp = -60 folium.GeoJson( gjhex, style_function=lambda x, mean_rsrp=mean_rsrp: { 'fillColor': map_color_rsrp(mean_rsrp), 'color': map_color_rsrp(mean_rsrp), 'weight': 1, 'fillOpacity': 0.5 }, #tooltip='tooltip' ).add_to(h3fillgroup) for hex in hexagons: p2 = h3.h3_to_geo(hex) #p2 = [45.3311, -121.7113] folium.Marker( p2, name='hex_names', icon=DivIcon( #icon_size=(150,36), #icon_anchor=(35,-45), icon_anchor=(35, 0), html='<div style="font-size: 6pt; color : black">' + str(hex) + '</div>', )).add_to(h3namegroup) #m.add_child(folium.CircleMarker(p2, radius=15)) polygons = h3.h3_set_to_multi_polygon([hex], geo_json=False) # flatten polygons into loops. outlines = [loop for polygon in polygons for loop in polygon] polyline = [outline + [outline[0]] for outline in outlines][0] lat.extend(map(lambda v: v[0], polyline)) lng.extend(map(lambda v: v[1], polyline)) polylines.append(polyline) for polyline in polylines: my_PolyLine = folium.PolyLine(locations=polyline, weight=1, color='blue') h3group.add_child(my_PolyLine) polylines = [] lat = [] lng = [] #polylines8 = [] for hex in hexagons8: p2 = h3.h3_to_geo(hex) folium.Marker( p2, name='hex_names', icon=DivIcon( #icon_size=(150,36), #icon_anchor=(35,-45), icon_anchor=(35, 0), html='<div style="font-size: 8pt; color : black">' + str(hex) + '</div>', )).add_to(h3r8namegroup) polygons = h3.h3_set_to_multi_polygon([hex], geo_json=False) # flatten polygons into loops. outlines = [loop for polygon in polygons for loop in polygon] polyline = [outline + [outline[0]] for outline in outlines][0] lat.extend(map(lambda v: v[0], polyline)) lng.extend(map(lambda v: v[1], polyline)) polylines.append(polyline) for polyline in polylines: my_PolyLine = folium.PolyLine(locations=polyline, weight=1, color='blue') h3r8group.add_child(my_PolyLine) # add possible tiles folium.TileLayer('cartodbpositron').add_to(m) folium.TileLayer('cartodbdark_matter').add_to(m) folium.TileLayer('openstreetmap').add_to(m) folium.TileLayer('Mapbox Bright').add_to(m) #folium.TileLayer('stamentoner').add_to(m) # add markers layer #marker_cluster = MarkerCluster().add_to(m) polargroup.add_to(m) #polar plot hsgroup.add_to(m) #hotspots dcgroup.add_to(m) #distance circles h3group.add_to(m) h3namegroup.add_to(m) h3fillgroup.add_to(m) m.keep_in_front(h3group) h3r8group.add_to(m) h3r8namegroup.add_to(m) # add the layer control folium.LayerControl(collapsed=False).add_to(m) m.save(hname + '//' + hname + '_map.html')