Skip to content

Commit cfa9994

Browse files
authored
Merge pull request #7 from G1OJS/V1.4
V1.4 Add timeline plots
2 parents c4abbd4 + c934a11 commit cfa9994

File tree

69 files changed

+129
-18
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

69 files changed

+129
-18
lines changed

pyproject.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "wsjt_all"
3-
version = "1.3.1"
3+
version = "1.4.0"
44
license = "MIT"
55
license-files = ["LICENSE"]
66

@@ -27,6 +27,8 @@ Homepage = "https://github.com/G1OJS/wsjt_all"
2727
Issues = "https://github.com/G1OJS/wsjt_all/issues"
2828

2929
[project.scripts]
30+
wsjt_all = "wsjt_all:wsjt_all"
31+
wsjt_all_live = "wsjt_all:wsjt_all_live"
3032
wsjt_all_ab = "wsjt_all:wsjt_all_ab"
3133
wsjt_all_ab_live = "wsjt_all:wsjt_all_ab_live"
3234

src/wsjt_all/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1-
from .prog_flow import wsjt_all_ab, wsjt_all_ab_live
1+
from .prog_flow import wsjt_all, wsjt_all_live, wsjt_all_ab, wsjt_all_ab_live
2+
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ def plot_snrs(ax, calls, decodes_A, decodes_B, show_best_snrs_only):
5656
if(show_best_snrs_only):
5757
series_x = [max(series_x)]
5858
series_y = [max(series_y)]
59-
ax.plot(series_x, series_y, color = colourseries[i % len(colourseries)] , marker ="o", alpha = 0.9)
59+
ax.plot(series_x, series_y, color = colourseries[i % len(colourseries)] , marker ="o", alpha = 0.9, lw = 0.2)
6060
ax.axline((0, 0), slope=1, color="black", linestyle=(0, (5, 5)))
6161
axrng = (min(ax.get_xlim()[0], ax.get_ylim()[0]), max(ax.get_xlim()[1], ax.get_ylim()[1]))
6262
ax.set_xlim(axrng)
@@ -90,7 +90,7 @@ def venn(ax, ns):
9090
ax.text(0.5+x2/2,0.5, f'B {ns[2]}', horizontalalignment='center',verticalalignment='center')
9191
ax.set_title("Number of callsigns in A only, A&B, B only")
9292

93-
def make_chart(plt, fig, axs, decodes_A, decodes_B, session_info, show_best_snrs_only = False):
93+
def make_chart_dual(plt, fig, axs, decodes_A, decodes_B, session_info, show_best_snrs_only = False):
9494
decs_A = time_window_decodes(decodes_A, session_info[0], session_info[1])
9595
decs_B = time_window_decodes(decodes_B, session_info[0], session_info[1])
9696
calls_a= get_callsigns(decs_A)

src/wsjt_all/plotter_single.py

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import matplotlib.pyplot as plt
2+
import random
3+
import os
4+
from wsjt_all.load_sessions import load_sessions, time_window_decodes,get_session_info_string
5+
global colourseries
6+
import datetime
7+
import mplcursors
8+
import hashlib
9+
10+
def hash_color(callsign, cmap=plt.cm.tab20):
11+
h = int(hashlib.sha1(callsign.encode()).hexdigest(), 16)
12+
return cmap(h % cmap.N)
13+
14+
def make_chart_single(plt, fig, axs, decodes_A, session_info):
15+
decs_A = time_window_decodes(decodes_A, session_info[0], session_info[1])
16+
session_info_string = get_session_info_string(session_info)
17+
18+
print("Plotting number of reports")
19+
timebins_mins = range(int((session_info[1] - session_info[0]) / 60) + 1)
20+
numbs = [0]*len(timebins_mins)
21+
call_rpts = {}
22+
for d in decodes_A:
23+
call_rpts.setdefault(d['oc'],[]).append({'t':d['t'], 'rp':d['rp']})
24+
tmins = int((d['t'] - session_info[0])/60)
25+
if(tmins in timebins_mins ): # this shouldn't return false? Check prior windowing?
26+
time_idx = timebins_mins.index(tmins)
27+
numbs[time_idx] += 1
28+
xc = [0.5+x for x in timebins_mins] # marker at centre of minute bin
29+
axs[0].plot(xc, numbs, marker = 'o', alpha = 0.9, lw = 0.5)
30+
31+
axs[0].set_title("Decode rate")
32+
axs[0].set_xlabel("Time (mins)")
33+
axs[0].set_ylabel("Number of decodes per minute")
34+
35+
36+
print("Plotting snr of reports")
37+
cols = []
38+
for i, c in enumerate(call_rpts):
39+
xc, yc = [], []
40+
cols.append(hash_color(c))
41+
for rpt in call_rpts[c]:
42+
if(rpt['t']>= session_info[0] and rpt['t']<= session_info[1]):
43+
xc.append((rpt['t'] - session_info[0]) / 60)
44+
yc.append(rpt['rp'])
45+
axs[1].plot(xc, yc, label = c, marker ="o", color = cols[i], alpha = 0.9, lw = 0.2)
46+
47+
axs[1].set_title("SNR per callsign")
48+
axs[1].set_xlabel("Time (mins)")
49+
axs[1].set_ylabel("SNR per callsign")
50+
51+
axs[0].set_xlim(0, int((session_info[1]-session_info[0])/60)+1)
52+
axs[1].set_xlim(0, int((session_info[1]-session_info[0])/60)+1)
53+
54+
fig.suptitle(f"Session: {session_info_string}")
55+
plt.tight_layout()
56+
57+

src/wsjt_all/prog_flow.py

Lines changed: 55 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22
import os
33
import datetime
44
import matplotlib.pyplot as plt
5-
from .plotter import make_chart, save_chart, init_colours
5+
from .plotter_dual import make_chart_dual, save_chart, init_colours
6+
from .plotter_single import make_chart_single
67
from .load_sessions import load_sessions, load_overlapping_sessions, get_session_info_string
78

89
def check_config():
@@ -24,18 +25,18 @@ def check_config():
2425
print("A wsjt_all.ini file has been created, but please edit the paths to point to the two ALL.txt files you want to compare.")
2526
print("Exiting program")
2627

27-
def plot_all_historic(allfilepath_A, allfilepath_B, session_guard_seconds, show_best_snrs_only):
28+
def plot_all_historic_dual(allfilepath_A, allfilepath_B, session_guard_seconds, show_best_snrs_only):
2829
sessions_AB, decodes_A, decodes_B = load_overlapping_sessions(allfilepath_A, allfilepath_B, session_guard_seconds)
2930
init_colours()
30-
print("Plotting sessions:")
31-
for session_info in sessions_AB:
31+
for i, session_info in enumerate(sessions_AB):
3232
session_info_string = get_session_info_string(session_info)
33+
print(f"Plotting session {i} of {len(sessions_AB)}: {session_info_string}")
3334
fig, axs = plt.subplots(3,1, figsize=(7, 9), height_ratios = (0.1,1,1))
34-
make_chart(plt, fig, axs, decodes_A, decodes_B, session_info, show_best_snrs_only)
35+
make_chart_dual(plt, fig, axs, decodes_A, decodes_B, session_info, show_best_snrs_only)
3536
save_chart(plt, session_info_string+".png")
3637
plt.close()
3738

38-
def plot_live(allfilepath_A, allfilepath_B, session_guard_seconds, plot_window_seconds, show_best_snrs_only):
39+
def plot_live_dual(allfilepath_A, allfilepath_B, session_guard_seconds, plot_window_seconds, show_best_snrs_only):
3940
fig, axs = plt.subplots(3,1, figsize=(7, 9), height_ratios = (0.1,1,1))
4041
plt.ion()
4142
init_colours()
@@ -52,9 +53,37 @@ def plot_live(allfilepath_A, allfilepath_B, session_guard_seconds, plot_window_s
5253
bm = sessions_A[-1][2]
5354
session_info=(ts,te,bm)
5455
axs[0].cla(), axs[1].cla(), axs[2].cla()
55-
make_chart(plt, fig, axs, decodes_A, decodes_B, session_info, show_best_snrs_only)
56+
make_chart_dual(plt, fig, axs, decodes_A, decodes_B, session_info, show_best_snrs_only)
5657
plt.pause(5)
5758

59+
def plot_live_single(allfilepath_A, session_guard_seconds, plot_window_seconds, show_best_snrs_only):
60+
fig, axs = plt.subplots(2,1, figsize=(6, 9), height_ratios = (1,1))
61+
plt.ion()
62+
print("Waiting for live session data")
63+
while(True):
64+
t_recent = datetime.datetime.now().timestamp() - plot_window_seconds * 3 # allow for delay in receiving live spots
65+
decodes_A, sessions_A = load_sessions(allfilepath_A, session_guard_seconds, skip_all_before = t_recent)
66+
if(len(sessions_A)>0):
67+
te = sessions_A[-1][1]
68+
ts = te - plot_window_seconds
69+
bm = sessions_A[-1][2]
70+
session_info=(ts,te,bm)
71+
axs[0].cla(), axs[1].cla()
72+
make_chart_single(plt, fig, axs, decodes_A, session_info, show_best_snrs_only)
73+
plt.pause(5)
74+
75+
def plot_all_historic_single(allfilepath_A, session_guard_seconds):
76+
decodes_A, sessions_A = load_sessions(allfilepath_A, session_guard_seconds)
77+
for i, session_info in enumerate(sessions_A):
78+
if(session_info[1] > session_info[0] and len(sessions_A)>2):
79+
session_info_string = get_session_info_string(session_info)
80+
print(f"Plotting session {i} of {len(sessions_A)}: {session_info_string}")
81+
fig, axs = plt.subplots(2,1, figsize=(6, 9), height_ratios = (1,1))
82+
make_chart_single(plt, fig, axs, decodes_A, session_info)
83+
save_chart(plt, session_info_string+"_timeline.png")
84+
plt.close()
85+
86+
5887
def run(option):
5988
if(check_config()):
6089
config = configparser.ConfigParser()
@@ -63,13 +92,26 @@ def run(option):
6392
session_guard_seconds = int(config.get("settings","session_guard_seconds"))
6493
live_plot_window_seconds = int(config.get("settings","live_plot_window_seconds"))
6594
show_best_snrs_only = (config.get("settings","show_best_snrs_only") == "Y")
66-
if(option=="hist"):
67-
plot_all_historic(allfilepath_A, allfilepath_B, session_guard_seconds, show_best_snrs_only)
68-
else:
69-
plot_live(allfilepath_A, allfilepath_B, session_guard_seconds, live_plot_window_seconds, show_best_snrs_only)
95+
if(option=="hist_single"):
96+
plot_all_historic_single(allfilepath_A, session_guard_seconds)
97+
if(option=="hist_ab"):
98+
plot_all_historic_dual(allfilepath_A, allfilepath_B, session_guard_seconds, show_best_snrs_only)
99+
if(option=="live_ab"):
100+
plot_live_dual(allfilepath_A, allfilepath_B, session_guard_seconds, live_plot_window_seconds, show_best_snrs_only)
101+
if(option=="live_single"):
102+
plot_live_single(allfilepath_A,session_guard_seconds, live_plot_window_seconds,False)
103+
104+
105+
def wsjt_all():
106+
run("hist_single")
107+
108+
def wsjt_all_live():
109+
run("live_single")
70110

71111
def wsjt_all_ab():
72-
run("hist")
112+
run("hist_ab")
73113

74114
def wsjt_all_ab_live():
75-
run("live")
115+
run("live_ab")
116+
117+

tests/example time plot 2.PNG

119 KB

tests/example time plot.PNG

148 KB
317 KB
292 KB
39.7 KB

0 commit comments

Comments
 (0)