In [1]:
%matplotlib inline
# %config InlineBackend.figure_format = 'svg'
from IPython.core.pylabtools import figsize
from mpl_toolkits.basemap import Basemap
import matplotlib.pyplot as plt
import numpy as np
import adif
from math import pi,radians
from mlocs import toLoc
from azutil import to_polar, relative_offset_qth, all_dxcc, loc_for_dxcc
import seaborn
figsize(9,9)
Set these variables :
my_qth
- to your current operating locationmy_log_file
- to the path to the ADIF file to useall_qths
- a list of locators to plot as places of interest on the graph.
In [2]:
my_qth = 'IO91vk'
my_log_file = 'log.adi' #'contacts.adi'
all_qths = [ my_qth ] #,'CM86xx','DO13' ] # list of locations you operate from uses first as center
print ('My QTH: {}, all QTHs: {}.'.format(my_qth,', '.join(all_qths)))
In [3]:
def draw_qth(m,locs,marker='o',**kwargs):
''' Draw a series of locations on m. locs is array of maidenhead locators.'''
coords = [ toLoc(loc) for loc in locs ]
coords = [ m(c[1],c[0]) for c in coords ] # transpose to map coords
for x,y in coords:
m.plot(x,y,marker,linestyle='None',**kwargs)
pass
def draw_circle(m, qth0, qth_list, **kwargs):
'''draw a great circle 'arc' to each locator in qth_list originating at qth0'''
defaults = {'alpha' :0.75, 'color':'lightblue','linewidth':0.5}
lat_0,lon_0 = toLoc(qth0)
coords = [ toLoc(loc) for loc in qth_list ]
for k,v in defaults.iteritems():
if k not in kwargs:
kwargs[k] = v
for lon,lat in [ (e[1], e[0]) for e in coords]:
m.drawgreatcircle (lon_0,lat_0,lon,lat,**kwargs)
Let's load my log from ADIF file.
Alternate Approach - build a dictionary of DXCC entities with list of grids. None if no GS. Add defaults. Average.
In [4]:
adif_reader = adif.ADIFReader(open (my_log_file,'rU'))
ad_contacts = adif_reader.load()
'''TODO clean up extras'''
ad_all_contact_dxcc = set([ q['dxcc'] for q in ad_contacts if 'dxcc' in q])
ad_contact_grids = set([ q['gridsquare'] for q in ad_contacts if 'gridsquare' in q])
ad_plotted_by_grid = set([ q['dxcc'] for q in ad_contacts if 'gridsquare' in q])
ad_plot_by_dxcc = ad_all_contact_dxcc.difference(ad_plotted_by_grid)
ad_dxcc_grids = [ loc_for_dxcc(d) for d in ad_plot_by_dxcc ]
#ad_no_grid = [ q for q in ad_contacts if 'gridsquare' not in q ]
all_dxcc_locs = [ loc_for_dxcc(d) for d in all_dxcc() ]
ctc_dxcc_locs = [ loc_for_dxcc(d) for d in ad_all_contact_dxcc ]
In [5]:
def azmap(qth):
proj='aeqd'
#proj='ortho'
lat,lon = toLoc(my_qth)
m = Basemap(projection=proj,lat_0=lat,lon_0=lon)
m.drawmapboundary()
m.drawcountries(linewidth=0.25)
m.drawcoastlines(linewidth=0.5)
m.drawparallels(np.arange(-80.0,81,20),linewidth=0.25)
m.drawmeridians(np.arange(-180,180,20),linewidth=0.25)
if False: m.nightshade(dt.datetime.utcnow(),alpha=0.2)
plt.title(qth)
return m
In [6]:
print ('Number of grid contacts: \t{}\nNumber of DXCC contacts: \t{}\nTotal Contacts: \t\t{}'.format(
len(ad_contact_grids),
len(ad_dxcc_grids),
len(ad_contacts)
))
In [7]:
m = azmap(my_qth)
# Draw GCR lines to each contact
draw_circle(m, my_qth, ad_contact_grids)
draw_circle(m, my_qth, ad_dxcc_grids)
# Draw our markers for contacts.
draw_qth( m, ad_contact_grids, color='blue',alpha=0.5)
draw_qth( m, all_qths, color='red', alpha=0.5, marker='8')
draw_qth( m, ad_dxcc_grids, alpha=0.5, marker='h',color='yellow')
title_text = plt.title('Contacts - {}'.format(my_qth))
In [8]:
m = azmap(my_qth)
plt.title('All DXCC from {}'.format(my_qth))
draw_circle(m, my_qth, all_dxcc_locs)
draw_qth(m,all_dxcc_locs,color='blue',alpha=0.5)
draw_qth(m,ctc_dxcc_locs,color='red',alpha=0.5)
These functions let us build a dictionary of all contacts per DXCC so we can average the location for the single bearing we will use to represent that DXCC on the final polar histogram.
In [9]:
def insert_ctc(d, dx,lonlat):
if dx not in d:
d[dx] = list()
d[dx].append( np.array([ lonlat[1],lonlat[0] ] ) )
ctc_dict = dict()
no_dx = 0
for c in ad_contacts:
if 'dxcc' in c:
dx = int(c['dxcc'])
if dx > 0:
if 'gridsquare' in c:
g = c['gridsquare']
else:
g = loc_for_dxcc(dx)
insert_ctc(ctc_dict,dx,toLoc(g))
else:
no_dx += 1
print ('{} has no DXCC'.format(c['callsign']))
# Average each DXCC co-ordinate list to a single lat lon
for dx in ctc_dict:
ctc_dict[dx] = np.mean(ctc_dict[dx],axis=0)
In [10]:
ctc_bearings = [ to_polar(*relative_offset_qth(m,my_qth,lonlat_dest=ctc_dict[dx]))[1] for dx in ctc_dict ]
dxcc_bearings = [ to_polar(*relative_offset_qth(m,my_qth,grid_dest=dxcc_loc))[1] for dxcc_loc in all_dxcc_locs ]
print 'Contacted DXCCs : ',len(ctc_bearings),'/', len(dxcc_bearings)
In [11]:
nbins = 16
def setup_polar_plot():
polar=True
ax = plt.subplot(111,polar=polar)
if polar:
ax.set_theta_zero_location('N')
ax.set_theta_direction(-1)
ax.set_theta_direction('clockwise')
ax.set_theta_zero_location('N')
else:
ax.set_yscale('log')
return ax
def bars(ax,theta,r,**kwargs):
w = 2*pi/len(theta)*0.95
ax.bar(left=theta,height=r,width=w,alpha=0.5,linewidth=0,align='center',**kwargs)
def bins(nbins=nbins):
# return np.linspace(0,2*pi,nbins)
return np.linspace(pi/nbins,(2*pi)-(pi/nbins),nbins)
def to_bin(bearing,nbins=nbins):
bsz = (2*pi)/nbins
return (bearing/bsz)%nbins
In [12]:
from math import degrees
dxcc_data = np.empty(nbins)
dxcc_data.fill(0)
ctc_data = np.empty(nbins)
ctc_data.fill(0)
''' Accumulate DXCCs by bearing '''
for theta in dxcc_bearings:
dxcc_data[to_bin(theta)] += 1
for theta in ctc_bearings:
ctc_data[to_bin(theta)] += 1
In [13]:
ax = setup_polar_plot()
bars(ax,bins(), ctc_data,color='red')
bars(ax,bins(), dxcc_data)
p = plt.title('Number of DXCCs by Azimuth Angle from {}\n'.format(my_qth))
l = plt.legend(['Contacts','All DXCC'])
In [14]:
ax = setup_polar_plot()
bars(ax,bins(), np.log10([ x+0.49 for x in ctc_data]),color='red')
bars(ax,bins(), np.log10(dxcc_data))
p = plt.title('Number of DXCCs ($log_{{10}}$) by Azimuth Angle from {}\n'.format(my_qth))
l = plt.legend(['My Contacts','All DXCC'])