def main():
    parser = argparse.ArgumentParser("analyze hotspots", add_help=True)
    parser.add_argument('-x', help='report to run', choices=['poc_reliability', 'poc_v10'], required=True)

    parser.add_argument('-c', '--challenges', help='number of challenges to analyze, default:500', default=500, type=int)
    parser.add_argument('-n', '--name', help='hotspot name to analyze with dashes-between-words')
    parser.add_argument('-a', '--address', help='hotspot address to analyze')

    args = parser.parse_args()

    H = Hotspots()
    hotspot = None
    if args.name:
        hotspot = H.get_hotspot_by_name(args.name)
        if hotspot is None:
            raise ValueError(f"could not find hotspot named '{args.name}' use dashes between words")
    elif args.address:
        hotspot = H.get_hotspot_by_addr(args.address)
        if hotspot is None:
            raise ValueError(f"could not find hotspot address '{args.address}' ")
    else:
        raise ValueError("must provide hotspot address '--address' or name '--name'")

    challenges = utils.load_challenges(hotspot['address'], args.challenges)
    if args.x == 'poc_reliability':
        poc_reliability(hotspot, challenges)
    elif args.x == 'poc_v10':
        pocv10_violations(hotspot, challenges)
Esempio n. 2
0
def main():
    parser = argparse.ArgumentParser("analyze hotspots", add_help=True)
    parser.add_argument(
        '-x',
        help='report to run',
        choices=['poc_reliability', 'poc_v10', 'poc_polar', 'poc_summary'],
        required=True)

    parser.add_argument('-c',
                        '--challenges',
                        help='number of challenges to analyze, default:500',
                        default=500,
                        type=int)
    parser.add_argument(
        '-n',
        '--name',
        help='hotspot name to analyze with dashes-between-words')
    parser.add_argument('-a', '--address', help='hotspot address to analyze')

    args = parser.parse_args()
    H = Hotspots()
    hotspot = None
    if args.name:
        hotspot = H.get_hotspot_by_name(args.name)
        if hotspot is None:
            raise ValueError(
                f"could not find hotspot named '{args.name}' use dashes between words"
            )
    elif args.address:
        hotspot = H.get_hotspot_by_addr(args.address)
        if hotspot is None:
            raise ValueError(
                f"could not find hotspot address '{args.address}' ")
    else:
        raise ValueError(
            "must provide hotspot address '--address' or name '--name'")

    challenges = utils.load_challenges(hotspot['address'], args.challenges)
    challenges = challenges[:args.challenges]
    if len(challenges) < 2:
        print(
            f"ERROR could not load challenges, either hotspot has been offline too long or you need to increase --challenge arguement"
        )
        return
    days, remainder = divmod(challenges[0]['time'] - challenges[-1]['time'],
                             3600 * 24)
    hours = int(round(remainder / 3600, 0))
    print(
        f"analyzing {len(challenges)} challenges from block {challenges[0]['height']}-{challenges[-1]['height']} over {days} days, {hours} hrs"
    )

    if args.x == 'poc_summary':
        poc_summary(hotspot, challenges)
    if args.x == 'poc_reliability':
        poc_reliability(hotspot, challenges)
    if args.x == 'poc_polar':
        poc_polar(hotspot, challenges)
    if args.x == 'poc_v10':
        pocv10_violations(hotspot, challenges)
Esempio n. 3
0
def main():
    parser = argparse.ArgumentParser("analyze hotspots", add_help=True)
    parser.add_argument('-x', help='report to run', choices=['beacons', 'witnesses', 'challenges'], required=True)
    parser.add_argument('-c', '--challenges', help='number of challenges to analyze, default:400', default=400, type=int)
    parser.add_argument('-n', '--name', help='hotspot name to analyze with dashes-between-words')
    parser.add_argument('-a', '--address', help='hotspot address to analyze')
    parser.add_argument('-d', '--details', help='return detailed report (listing each activity)', action='store_true')

    args = parser.parse_args()

    H = Hotspots()
    hotspot = None
    if args.name:
        hotspot = H.get_hotspot_by_name(args.name)
        if hotspot is None:
            raise ValueError(f"could not find hotspot named '{args.name}' use dashes between words")
    elif args.address:
        hotspot = H.get_hotspot_by_addr(args.address)
        if hotspot is None:
            raise ValueError(f"could not find hotspot address '{args.address}' ")
    else:
        raise ValueError("must provide hotspot address '--address' or name '--name'")

    challenges = utils.load_challenges(hotspot['address'], args.challenges)
    challenges = challenges[:args.challenges]
    if len(challenges) < 2:
        print(f"ERROR could not load challenges, either hotspot has been offline too long or you need to increase --challenge arguement")
        return
    days, remainder = divmod(challenges[0]['time'] - challenges[-1]['time'], 3600 * 24)
    hours = int(round(remainder / 3600, 0))
    print(f"analyzing {len(challenges)} challenges from block {challenges[0]['height']}-{challenges[-1]['height']} over {days} days, {hours} hrs")

    if args.x == 'beacons':
        transmit_details(hotspot, challenges, smry_only=not args.details)
    elif args.x == 'witnesses':
        witness_detail(hotspot, challenges, smry_only=not args.details)
    elif args.x == 'challenges':
        challenger_details(hotspot, challenges, smry_only=not args.details)
    else:
        print(f"unsupported report")
Esempio n. 4
0
def challenger_details(hotspot, chals, smry_only=False):
    haddr = hotspot['address']
    H = Hotspots()
    H.update_reference_hspot(address=haddr)
    hotspot = H.get_hotspot_by_addr(haddr)
    print(f"Hotspot: {hotspot['name']}")
    if not smry_only:
        print(f"{'time':14} | {'block':7} | blck Δ | {'challengee':25} | scale | rct | wtns ")
        # print("=" * 82)
    vars = utils.api_call(path='vars')['data']
    max_rct_age = vars['poc_v4_target_challenge_age']
    unsuspected_lone_wolfs = 0
    dense_challenges = 0

    num_poc_rcts = 0
    newest_block = 0
    oldest_block = 1e8
    prev_rct_block = None
    max_block_delta = 0
    block_deltas = []
    for c in chals:
        if c['challenger'] != hotspot['address']:
            continue

        newest_block = max(newest_block, c['height'])
        oldest_block = min(oldest_block, c['height'])
        transmitter = H.get_hotspot_by_addr(c['path'][0]['challengee'])
        num_poc_rcts += 1
        # time, transmitter, distance, val/inval, RU, reason inval
        time_str = dt.datetime.fromtimestamp(c['time']).isoformat()[5:19]
        time_str = time_str.replace('T', ' ')
        transmitter_name = transmitter['name']
        num_ws = len(c['path'][0]['witnesses'])
        w_str = 'NONE' if num_ws == 0 else f'{num_ws} '
        if transmitter['reward_scale'] <= 0.9:
            dense_challenges += 1
            if num_ws == 0:
                unsuspected_lone_wolfs += 1
        block_delta = 0
        block_delta_str = 'N/A'
        if prev_rct_block:
            block_delta = prev_rct_block - c['height']
            block_deltas.append(block_delta)
            block_delta_str = f"{block_delta}" + ('**' if block_delta > max_rct_age else '')
        max_block_delta = max(block_delta, max_block_delta)
        if not smry_only:
            print(f"{time_str:14} | {c['height']:7} | {block_delta_str:6} | {transmitter_name[:25]:25} | {transmitter['reward_scale']:5.2f} | {'YES' if c['path'][0]['receipt'] else 'no' :3} | {w_str:>4}")

        prev_rct_block = c['height']
    print()
    print(f"summary stats")
    print(f"challenger address:        {hotspot['address']}")
    print(f"challenger listening_addr: {hotspot['status']['listen_addrs'][0]}")
    # print(f'lone wolfs in dense areas: {unsuspected_lone_wolfs:<3d}/{dense_challenges:3d}')
    print(f"blocks between chalng avg: {(newest_block - oldest_block) / num_poc_rcts:.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)}")
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 poc_reliability(hotspot, challenges):
    """

    :param hotspot:
    :param challenges: list of challenges
    :return:
    """
    H = Hotspots()
    haddr = hotspot['address']

    days, remainder = divmod(challenges[0]['time'] - challenges[-1]['time'], 3600 * 24)
    hours = int(round(remainder / 3600, 0))
    print(f"analyzing {len(challenges)} challenges from block {challenges[0]['height']}-{challenges[-1]['height']} over {days} days, {hours} hrs")


    # iterate through challenges finding actual interactions with this hotspot
    results_tx = dict()  # key = tx addr, value = [pass, fail]
    results_rx = dict()  # key = rx addr, value = [pass, fail]
    for chal in challenges:
        pnext = chal['path'][-1]
        pnext_pass = pnext['witnesses'] or pnext['receipt']

        for p in chal['path'][:-1][::-1]:
            if pnext_pass or p['witnesses'] or p['receipt']:
                if pnext['challengee'] == haddr:
                    if p['challengee'] not in results_rx:
                        results_rx[p['challengee']] = [0, 0]
                    results_rx[p['challengee']][0 if pnext_pass else 1] += 1
                if p['challengee'] == haddr:
                    if pnext['challengee'] not in results_tx:
                        results_tx[pnext['challengee']] = [0, 0]
                    results_tx[pnext['challengee']][0 if pnext_pass else 1] += 1
                pnext_pass = True
            pnext = p

    hlat = hotspot['lat']
    hlon = hotspot['lng']

    def summary_table(results, hotspot_transmitting=False):

        other_pass = 0
        other_ttl = 0
        other_cnt = 0
        dist_min = 9999
        dist_max = 0

        if hotspot_transmitting:
            print(f"PoC hops from: {hotspot['name']}")
            print(f"{'to receiving hotspot':30} | {'dist km'} | {'heading'} | recv/ttl | recv % |")
        else:
            print(f"PoC hops to: {hotspot['name']}")
            print(f"{'from transmitting hotspot':30} | {'dist km'} | {'heading'} | recv/ttl | recv % |")
        print("-" * 72)

        for h in results.keys():
            ttl = results[h][0] + results[h][1]

            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

            print(f"{H.get_hotspot_by_addr(h)['name']:30} | {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} | {dist_min:4.1f}-{dist_max:2.0f} |   N/A   | {other_pass:3d}/{other_ttl:3d}  | {other_pass / other_ttl * 100:5.0f}% | ")

    summary_table(results_tx, hotspot_transmitting=True)
    print()
    print()
    summary_table(results_rx, hotspot_transmitting=False)
Esempio n. 7
0
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()
Esempio n. 8
0
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}")
Esempio n. 9
0
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_reliability(hotspot, challenges):
    """

    :param hotspot:
    :param challenges: list of challenges
    :return:
    """
    H = Hotspots()
    haddr = hotspot['address']

    # iterate through challenges finding actual interactions with this hotspot
    results_tx = dict()  # key = tx addr, value = [pass, fail]
    results_rx = dict()  # key = rx addr, value = [pass, fail]
    for chal in challenges:
        pnext = chal['path'][-1]
        pnext_pass = pnext['witnesses'] or pnext['receipt']

        for p in chal['path'][:-1][::-1]:
            if pnext_pass or p['witnesses'] or p['receipt']:
                if pnext['challengee'] == haddr:
                    if p['challengee'] not in results_rx:
                        results_rx[p['challengee']] = [0, 0]
                    results_rx[p['challengee']][0 if pnext_pass else 1] += 1
                if p['challengee'] == haddr:
                    if pnext['challengee'] not in results_tx:
                        results_tx[pnext['challengee']] = [0, 0]
                    results_tx[
                        pnext['challengee']][0 if pnext_pass else 1] += 1
                pnext_pass = True
            pnext = p

    hlat = hotspot['lat']
    hlon = hotspot['lng']

    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)

        # print in descending order
        sort_keys = [(results[r][0] + results[r][1], r) for r in results]
        sort_keys.sort(reverse=True)

        for h in [sk[1] for sk in sort_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}% | "
            )
        if all_ttl:
            print(f"{' ' * 40}{' ' * 10}         ---------------------")
            print(
                f"{' ' * 40}{' '*10}   TOTAL | {all_pass:3d}/{all_ttl:4d} | {all_pass / all_ttl * 100:5.0f}% | "
            )

    summary_table(results_tx, hotspot_transmitting=True)
    print()
    print()
    summary_table(results_rx, hotspot_transmitting=False)
Esempio n. 11
0
    parser = argparse.ArgumentParser("tax tools")
    parser.add_argument('-x',
                        choices=[
                            'refresh_hotspots', 'hnt_rewards', 'tax_lots',
                            'parse_trades', 'schedule_d'
                        ],
                        help="action to take",
                        required=True)
    parser.add_argument(
        '-n',
        '--name',
        help='hotspot name to analyze with dashes-between-words')
    parser.add_argument('-f', '--file', help='data file(s) for tax processing')
    parser.add_argument('-y', '--year', help='filter to a given tax year')
    args = parser.parse_args()
    H = Hotspots()
    hotspots = []
    if args.name:
        names = args.name.split(',')
        for name in names:
            hotspot = H.get_hotspot_by_name(name)
            if hotspot is None:
                raise ValueError(
                    f"could not find hotspot named '{name}' use dashes between words"
                )
            hotspots.append(hotspot)
    year = -1
    if args.year:
        year = int(args.year)
        print(f"running for tax year: {year}")
Esempio n. 12
0
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')