PyFT8 2.7.2__tar.gz → 2.7.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pyft8-2.7.2 → pyft8-2.7.4}/PKG-INFO +1 -1
- {pyft8-2.7.2 → pyft8-2.7.4}/PyFT8/gui.py +7 -7
- {pyft8-2.7.2 → pyft8-2.7.4}/PyFT8/mqtt.py +27 -18
- {pyft8-2.7.2 → pyft8-2.7.4}/PyFT8/pyft8.py +15 -9
- {pyft8-2.7.2 → pyft8-2.7.4}/PyFT8/time_utils.py +1 -1
- {pyft8-2.7.2 → pyft8-2.7.4}/PyFT8.egg-info/PKG-INFO +1 -1
- {pyft8-2.7.2 → pyft8-2.7.4}/pyproject.toml +1 -1
- {pyft8-2.7.2 → pyft8-2.7.4}/LICENSE +0 -0
- {pyft8-2.7.2 → pyft8-2.7.4}/MANIFEST.in +0 -0
- {pyft8-2.7.2 → pyft8-2.7.4}/PyFT8/__init__.py +0 -0
- {pyft8-2.7.2 → pyft8-2.7.4}/PyFT8/callhashes.py +0 -0
- {pyft8-2.7.2 → pyft8-2.7.4}/PyFT8/hamlib.py +0 -0
- {pyft8-2.7.2 → pyft8-2.7.4}/PyFT8/maidenhead.py +0 -0
- {pyft8-2.7.2 → pyft8-2.7.4}/PyFT8/pskr_upload.py +0 -0
- {pyft8-2.7.2 → pyft8-2.7.4}/PyFT8/receiver.py +0 -0
- {pyft8-2.7.2 → pyft8-2.7.4}/PyFT8/rigctrl.py +0 -0
- {pyft8-2.7.2 → pyft8-2.7.4}/PyFT8/transmitter.py +0 -0
- {pyft8-2.7.2 → pyft8-2.7.4}/PyFT8.egg-info/SOURCES.txt +0 -0
- {pyft8-2.7.2 → pyft8-2.7.4}/PyFT8.egg-info/dependency_links.txt +0 -0
- {pyft8-2.7.2 → pyft8-2.7.4}/PyFT8.egg-info/entry_points.txt +0 -0
- {pyft8-2.7.2 → pyft8-2.7.4}/PyFT8.egg-info/requires.txt +0 -0
- {pyft8-2.7.2 → pyft8-2.7.4}/PyFT8.egg-info/top_level.txt +0 -0
- {pyft8-2.7.2 → pyft8-2.7.4}/README.md +0 -0
- {pyft8-2.7.2 → pyft8-2.7.4}/setup.cfg +0 -0
- {pyft8-2.7.2 → pyft8-2.7.4}/tests/dev/CQ AAAA.py +0 -0
- {pyft8-2.7.2 → pyft8-2.7.4}/tests/dev/osd.py +0 -0
- {pyft8-2.7.2 → pyft8-2.7.4}/tests/dev/test_generate_wav.py +0 -0
- {pyft8-2.7.2 → pyft8-2.7.4}/tests/dev/test_loopback_performance.py +0 -0
- {pyft8-2.7.2 → pyft8-2.7.4}/tests/dev/view_worked_before.py +0 -0
- {pyft8-2.7.2 → pyft8-2.7.4}/tests/plot_baseline.py +0 -0
- {pyft8-2.7.2 → pyft8-2.7.4}/tests/spare.py +0 -0
- {pyft8-2.7.2 → pyft8-2.7.4}/tests/test_batch_and_live.py +0 -0
|
@@ -50,12 +50,12 @@ class Scrollbox:
|
|
|
50
50
|
for i in range(self.nlines):
|
|
51
51
|
self.lineartists[i].set_text(self.default_text)
|
|
52
52
|
|
|
53
|
-
def list_print(self, lst):
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
self.lineartists[i].set_color(
|
|
53
|
+
def list_print(self, lst, colors = None):
|
|
54
|
+
for i, txt in enumerate(lst[:self.nlines]):
|
|
55
|
+
if txt != self.lineartists[i].get_text():
|
|
56
|
+
self.lineartists[i].set_text(txt)
|
|
57
|
+
col = colors[i] if colors is not None and i < len(colors) else 'white'
|
|
58
|
+
self.lineartists[i].set_color(col)
|
|
59
59
|
for i in range(len(lst), self.nlines):
|
|
60
60
|
if self.lineartists[i].get_text() != '':
|
|
61
61
|
self.lineartists[i].set_text('')
|
|
@@ -165,7 +165,7 @@ class Gui:
|
|
|
165
165
|
self.band_stats = Scrollbox(self.fig, [L['pmargin'], wf_top+L['vsep1'], L['sidebar_width'], L['banner_height']], nlines = 4, monospace = True)
|
|
166
166
|
self.band_stats.ax.text(-0.2,0.75,'Tx')
|
|
167
167
|
self.band_stats.ax.text(-0.2,0.25,'Rx')
|
|
168
|
-
self.band_stats.ax.set_title(f"Spots to/from {config['station']['grid'][:4]}", fontsize = 10)
|
|
168
|
+
self.band_stats.ax.set_title(f"Spots to/from {config['station']['grid'][:4]} <15 mins", fontsize = 10)
|
|
169
169
|
|
|
170
170
|
# console
|
|
171
171
|
self.console = Scrollbox(self.fig, [wf_left, wf_top+L['vsep1'], 1-wf_left-L['pmargin'], L['banner_height']])
|
|
@@ -10,10 +10,10 @@ import pickle
|
|
|
10
10
|
|
|
11
11
|
class DiskDict:
|
|
12
12
|
def __init__(self, file):
|
|
13
|
+
self.lock = threading.Lock()
|
|
13
14
|
self.file = file
|
|
14
15
|
self.data = {}
|
|
15
16
|
self.load()
|
|
16
|
-
self.lock = threading.Lock()
|
|
17
17
|
threading.Thread(target = self._autosave, daemon = True).start()
|
|
18
18
|
|
|
19
19
|
def _autosave(self, autosave_period = 15):
|
|
@@ -22,22 +22,30 @@ class DiskDict:
|
|
|
22
22
|
self.save()
|
|
23
23
|
|
|
24
24
|
def load(self):
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
self.
|
|
25
|
+
with self.lock:
|
|
26
|
+
if(os.path.exists(self.file)):
|
|
27
|
+
with open(f"{self.file}","rb") as f:
|
|
28
|
+
self.data = pickle.load(f)
|
|
28
29
|
|
|
29
30
|
def save(self):
|
|
30
31
|
with self.lock:
|
|
31
|
-
|
|
32
|
+
tmp_file = f"{self.file}.tmp"
|
|
33
|
+
with open(tmp_file, "wb") as f:
|
|
32
34
|
pickle.dump(self.data, f)
|
|
33
|
-
|
|
35
|
+
f.flush()
|
|
36
|
+
os.fsync(f.fileno())
|
|
37
|
+
os.replace(tmp_file, self.file)
|
|
38
|
+
|
|
34
39
|
class PSKR_MQTT_listener:
|
|
35
40
|
def __init__(self, config_folder, my_call, home_square):
|
|
36
41
|
self.my_call = my_call
|
|
37
42
|
self.hearing_me = DiskDict(f"{config_folder}/hearing_me.pkl")
|
|
43
|
+
self.heard_by_me = DiskDict(f"{config_folder}/heard_by_me.pkl")
|
|
44
|
+
self.hearing_me_new = []
|
|
45
|
+
self.heard_by_me_new = []
|
|
38
46
|
self.home_square = home_square
|
|
39
47
|
self.callsign_cache = DiskDict(f"{config_folder}/callsign_cache.pkl")
|
|
40
|
-
self.band_TxRx_homecall_report_times = DiskDict(f"{config_folder}/
|
|
48
|
+
self.band_TxRx_homecall_report_times = DiskDict(f"{config_folder}/report_times.pkl")
|
|
41
49
|
self.band_TxRx_homecall_couniTxRxemotes = {}
|
|
42
50
|
self.home_activity = {}
|
|
43
51
|
self.home_most_remotes = {}
|
|
@@ -68,13 +76,22 @@ class PSKR_MQTT_listener:
|
|
|
68
76
|
for iTxRx, c in enumerate([sc, rc]):
|
|
69
77
|
call, loc = c
|
|
70
78
|
self.callsign_cache.data[call] = loc
|
|
79
|
+
tnow = time.time()
|
|
71
80
|
if self.home_square in loc:
|
|
72
81
|
key = (d['b'], iTxRx, call)
|
|
73
82
|
self.band_TxRx_homecall_report_times.data.setdefault(key, [])
|
|
74
|
-
self.band_TxRx_homecall_report_times.data[key].append(
|
|
83
|
+
self.band_TxRx_homecall_report_times.data[key].append(tnow)
|
|
75
84
|
if d['sc'] == self.my_call:
|
|
76
|
-
self.hearing_me.data.setdefault(d['b'], {})
|
|
77
|
-
|
|
85
|
+
self.hearing_me.data.setdefault(d['b'], {})
|
|
86
|
+
if d['rc'] not in self.hearing_me.data:
|
|
87
|
+
self.hearing_me_new.append(d['rc'])
|
|
88
|
+
self.hearing_me.data[d['b']][d['rc']] = {'t': tnow,'rp': d['rp'],'c': d['rc']}
|
|
89
|
+
if d['rc'] == self.my_call:
|
|
90
|
+
self.heard_by_me.data.setdefault(d['b'], {})
|
|
91
|
+
if d['sc'] not in self.heard_by_me.data:
|
|
92
|
+
self.heard_by_me_new.append(d['sc'])
|
|
93
|
+
self.heard_by_me.data[d['b']][d['sc']] = {'t': tnow,'rp': d['rp'],'c': d['sc']}
|
|
94
|
+
|
|
78
95
|
def count_activity(self):
|
|
79
96
|
import numpy as np
|
|
80
97
|
while True:
|
|
@@ -107,14 +124,6 @@ class PSKR_MQTT_listener:
|
|
|
107
124
|
if nremotes>self.home_most_remotes[b][iTxRx][1]:
|
|
108
125
|
self.home_most_remotes[b][iTxRx] = (c, nremotes)
|
|
109
126
|
|
|
110
|
-
# prune the hearing me list to <SPOTLIFE
|
|
111
|
-
for b in self.hearing_me.data:
|
|
112
|
-
newdict = {}
|
|
113
|
-
for c in self.hearing_me.data[b]:
|
|
114
|
-
if (time.time() - self.hearing_me.data[b][c]['t']) < SPOTLIFE:
|
|
115
|
-
newdict[c] = self.hearing_me.data[b][c]
|
|
116
|
-
self.hearing_me.data[b] = newdict
|
|
117
|
-
|
|
118
127
|
def get_spot_counts(self, band, call):
|
|
119
128
|
tx_reports = self.band_TxRx_homecall_report_times.data.get((band, 0, call), [])
|
|
120
129
|
rx_reports = self.band_TxRx_homecall_report_times.data.get((band, 1, call), [])
|
|
@@ -14,9 +14,10 @@ from PyFT8.hamlib import Rig_hamlib
|
|
|
14
14
|
from PyFT8.mqtt import PSKR_MQTT_listener
|
|
15
15
|
import PyFT8.maidenhead as maidenhead
|
|
16
16
|
|
|
17
|
-
VER = '2.7.
|
|
17
|
+
VER = '2.7.4'
|
|
18
18
|
|
|
19
19
|
MAX_TX_START_SECONDS = 2.5
|
|
20
|
+
SPOTLIFE = 5*60
|
|
20
21
|
rig, gui, qso, adif_logging, pskr_info, pskr_upload = None, None, None, None, None, None
|
|
21
22
|
busy_profile, hearing_me = None, None
|
|
22
23
|
|
|
@@ -263,7 +264,7 @@ def on_rx_decode(c):
|
|
|
263
264
|
write_all_txt_row(message)
|
|
264
265
|
|
|
265
266
|
def on_rx_busy_profile(busy_profile_new, cycle):
|
|
266
|
-
global busy_profile
|
|
267
|
+
global busy_profile, clearest_frequency
|
|
267
268
|
if output_device_idx is None:
|
|
268
269
|
return
|
|
269
270
|
if busy_profile is not None:
|
|
@@ -273,7 +274,7 @@ def on_rx_busy_profile(busy_profile_new, cycle):
|
|
|
273
274
|
idx = np.argmin(busy_profile[f0_idx:fn_idx])
|
|
274
275
|
clearest_frequency = (f0_idx + idx) * audio_in.df
|
|
275
276
|
busy_profile = busy_profile_new
|
|
276
|
-
|
|
277
|
+
console_print(f"[on_busy] Set Tx freq to {clearest_frequency:6.1f}")
|
|
277
278
|
|
|
278
279
|
#============= Callbacks for GUI ==========================================================
|
|
279
280
|
def on_gui_sidebars_refresh(gui):
|
|
@@ -306,14 +307,19 @@ def on_gui_sidebars_refresh(gui):
|
|
|
306
307
|
gui.band_stats.scroll_print(f"{call:<7} {rx_lead[0]:<7}", color = '#b6f0c6')
|
|
307
308
|
gui.band_stats.scroll_print(f"{n_spotted:<7} {rx_lead[1]:<7}", color = '#b6f0c6')
|
|
308
309
|
|
|
309
|
-
#refresh hearing me
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
310
|
+
#refresh hearing me / heard by me panel
|
|
311
|
+
cycle = global_time_utils.curr_cycle_from_time()
|
|
312
|
+
data = pskr_info.hearing_me.data if cycle == 1 else pskr_info.heard_by_me.data
|
|
313
|
+
new_calls = pskr_info.hearing_me_new if cycle == 1 else pskr_info.heard_by_me_new
|
|
314
|
+
txts, cols = [f"Hearing me <{SPOTLIFE/60:.0f} mins" if cycle==1 else f"Heard by me <{SPOTLIFE/60:.0f} mins"], ['white']
|
|
315
|
+
if b is not None and b in data:
|
|
316
|
+
hm = [h for h in data[b].values() if (time.time() - h['t']) < SPOTLIFE]
|
|
313
317
|
for h in hm:
|
|
314
318
|
geo_text = geo_text = get_geo_text(h['c'])
|
|
315
|
-
|
|
316
|
-
|
|
319
|
+
txts.append(f"{h['c']:<7} {int(h['rp']):+03d} {geo_text:<12}")
|
|
320
|
+
col = 'white' if h['c'] in new_calls else 'lime'
|
|
321
|
+
cols.append(col)
|
|
322
|
+
gui.hm.list_print(txts, cols)
|
|
317
323
|
|
|
318
324
|
def on_gui_control_click(btn_def):
|
|
319
325
|
btn_action = btn_def['action']
|
|
@@ -12,7 +12,7 @@ class Time_utils:
|
|
|
12
12
|
|
|
13
13
|
def curr_cycle_from_time(self):
|
|
14
14
|
t = time.time()
|
|
15
|
-
return int((t % 2*self.cycle_seconds) / self.cycle_seconds)
|
|
15
|
+
return int((t % (2*self.cycle_seconds)) / self.cycle_seconds)
|
|
16
16
|
|
|
17
17
|
def cyclestart(self, t):
|
|
18
18
|
cst = self.cycle_seconds * int(t / self.cycle_seconds)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|