PyFT8 2.7.6__tar.gz → 2.7.7__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.6 → pyft8-2.7.7}/PKG-INFO +1 -1
- {pyft8-2.7.6 → pyft8-2.7.7}/PyFT8/gui.py +4 -1
- {pyft8-2.7.6 → pyft8-2.7.7}/PyFT8/mqtt.py +24 -29
- {pyft8-2.7.6 → pyft8-2.7.7}/PyFT8/pyft8.py +38 -24
- {pyft8-2.7.6 → pyft8-2.7.7}/PyFT8.egg-info/PKG-INFO +1 -1
- {pyft8-2.7.6 → pyft8-2.7.7}/pyproject.toml +1 -1
- {pyft8-2.7.6 → pyft8-2.7.7}/LICENSE +0 -0
- {pyft8-2.7.6 → pyft8-2.7.7}/MANIFEST.in +0 -0
- {pyft8-2.7.6 → pyft8-2.7.7}/PyFT8/__init__.py +0 -0
- {pyft8-2.7.6 → pyft8-2.7.7}/PyFT8/callhashes.py +0 -0
- {pyft8-2.7.6 → pyft8-2.7.7}/PyFT8/hamlib.py +0 -0
- {pyft8-2.7.6 → pyft8-2.7.7}/PyFT8/maidenhead.py +0 -0
- {pyft8-2.7.6 → pyft8-2.7.7}/PyFT8/pskr_upload.py +0 -0
- {pyft8-2.7.6 → pyft8-2.7.7}/PyFT8/receiver.py +0 -0
- {pyft8-2.7.6 → pyft8-2.7.7}/PyFT8/rigctrl.py +0 -0
- {pyft8-2.7.6 → pyft8-2.7.7}/PyFT8/time_utils.py +0 -0
- {pyft8-2.7.6 → pyft8-2.7.7}/PyFT8/transmitter.py +0 -0
- {pyft8-2.7.6 → pyft8-2.7.7}/PyFT8.egg-info/SOURCES.txt +0 -0
- {pyft8-2.7.6 → pyft8-2.7.7}/PyFT8.egg-info/dependency_links.txt +0 -0
- {pyft8-2.7.6 → pyft8-2.7.7}/PyFT8.egg-info/entry_points.txt +0 -0
- {pyft8-2.7.6 → pyft8-2.7.7}/PyFT8.egg-info/requires.txt +0 -0
- {pyft8-2.7.6 → pyft8-2.7.7}/PyFT8.egg-info/top_level.txt +0 -0
- {pyft8-2.7.6 → pyft8-2.7.7}/README.md +0 -0
- {pyft8-2.7.6 → pyft8-2.7.7}/setup.cfg +0 -0
- {pyft8-2.7.6 → pyft8-2.7.7}/tests/dev/CQ AAAA.py +0 -0
- {pyft8-2.7.6 → pyft8-2.7.7}/tests/dev/osd.py +0 -0
- {pyft8-2.7.6 → pyft8-2.7.7}/tests/dev/test_generate_wav.py +0 -0
- {pyft8-2.7.6 → pyft8-2.7.7}/tests/dev/test_loopback_performance.py +0 -0
- {pyft8-2.7.6 → pyft8-2.7.7}/tests/dev/view_worked_before.py +0 -0
- {pyft8-2.7.6 → pyft8-2.7.7}/tests/plot_baseline.py +0 -0
- {pyft8-2.7.6 → pyft8-2.7.7}/tests/spare.py +0 -0
- {pyft8-2.7.6 → pyft8-2.7.7}/tests/test_batch_and_live.py +0 -0
|
@@ -145,6 +145,7 @@ class Gui:
|
|
|
145
145
|
self.msg_boxes = {}
|
|
146
146
|
self.decode_queue = queue.Queue()
|
|
147
147
|
self.make_layout(config)
|
|
148
|
+
self.display_cycle = 0
|
|
148
149
|
self.ani = FuncAnimation(self.fig, self._animate, interval = 40, frames=(100000), blit=True)
|
|
149
150
|
|
|
150
151
|
def set_bandstats_title(self, txt):
|
|
@@ -195,7 +196,8 @@ class Gui:
|
|
|
195
196
|
|
|
196
197
|
|
|
197
198
|
def refresh_sidebars(self):
|
|
198
|
-
self.
|
|
199
|
+
self.display_cycle = (self.display_cycle + 1) %2
|
|
200
|
+
self.on_gui_sidebars_refresh(self, self.display_cycle)
|
|
199
201
|
|
|
200
202
|
def add_message_box(self, message):
|
|
201
203
|
self.decode_queue.put(message)
|
|
@@ -217,6 +219,7 @@ class Gui:
|
|
|
217
219
|
self._display_message_box(self.decode_queue.get())
|
|
218
220
|
if (frame % 10 == 0):
|
|
219
221
|
self._tidy_msg_boxes()
|
|
222
|
+
if (frame % 30 == 0):
|
|
220
223
|
self.refresh_sidebars()
|
|
221
224
|
return [self.image, *self.ax_wf.patches, *self.ax_wf.texts, *self.band_stats.lineartists, *self.console.lineartists, *self.hm.lineartists,
|
|
222
225
|
*[bb.label for bb in self.button_boxes], *[bb.label2 for bb in self.button_boxes]]
|
|
@@ -35,8 +35,8 @@ class DiskDict:
|
|
|
35
35
|
os.replace(tmp_file, self.file)
|
|
36
36
|
|
|
37
37
|
class PSKR_MQTT_listener:
|
|
38
|
-
def __init__(self, config_folder, my_call, home_square,
|
|
39
|
-
self.
|
|
38
|
+
def __init__(self, config_folder, my_call, home_square, pskr_refresh_mins):
|
|
39
|
+
self.pskr_refresh_mins = pskr_refresh_mins
|
|
40
40
|
self.my_call = my_call
|
|
41
41
|
self.hearing_me = DiskDict(f"{config_folder}/hearing_me.pkl")
|
|
42
42
|
self.heard_by_me = DiskDict(f"{config_folder}/heard_by_me.pkl")
|
|
@@ -45,7 +45,6 @@ class PSKR_MQTT_listener:
|
|
|
45
45
|
self.home_square = home_square
|
|
46
46
|
self.callsign_cache = DiskDict(f"{config_folder}/callsign_cache.pkl")
|
|
47
47
|
self.band_TxRx_homecall_report_times = DiskDict(f"{config_folder}/report_times.pkl")
|
|
48
|
-
self.band_TxRx_homecall_couniTxRxemotes = {}
|
|
49
48
|
self.home_activity = {}
|
|
50
49
|
self.home_most_remotes = {}
|
|
51
50
|
self.lock = threading.Lock()
|
|
@@ -66,30 +65,37 @@ class PSKR_MQTT_listener:
|
|
|
66
65
|
client.subscribe(f"pskr/filter/v2/+/FT8/+/+/{self.home_square}/#")
|
|
67
66
|
client.subscribe(f"pskr/filter/v2/+/FT8/+/+/+/{self.home_square}/#")
|
|
68
67
|
|
|
68
|
+
def store_best_location(self, call, loc):
|
|
69
|
+
existing_loc = self.callsign_cache.data.get(call, '')
|
|
70
|
+
if len(loc) > len(existing_loc):
|
|
71
|
+
self.callsign_cache.data[call] = loc
|
|
72
|
+
|
|
73
|
+
def add_homespots_record(self, key, t):
|
|
74
|
+
self.band_TxRx_homecall_report_times.data.setdefault(key, [])
|
|
75
|
+
self.band_TxRx_homecall_report_times.data[key].append(t)
|
|
76
|
+
|
|
77
|
+
def add_myspots_record(self, data, band, call, t, rp):
|
|
78
|
+
data.setdefault(band, {})
|
|
79
|
+
data[band][call] = {'t': t,'rp':rp,'c':call}
|
|
80
|
+
|
|
69
81
|
def on_message(self, client, userdata, msg):
|
|
70
82
|
try:
|
|
71
83
|
d = literal_eval(msg.payload.decode())
|
|
72
84
|
except:
|
|
73
85
|
return
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
def add_spot(self, d):
|
|
86
|
+
tnow = time.time()
|
|
77
87
|
sc, rc = (d['sc'], d['sl']), (d['rc'], d['rl'])
|
|
78
|
-
for iTxRx,
|
|
79
|
-
call, loc =
|
|
80
|
-
self.
|
|
81
|
-
tnow = time.time()
|
|
88
|
+
for iTxRx, call_loc in enumerate([sc, rc]):
|
|
89
|
+
call, loc = call_loc
|
|
90
|
+
self.store_best_location(call, loc)
|
|
82
91
|
if self.home_square in loc:
|
|
83
|
-
|
|
84
|
-
self.band_TxRx_homecall_report_times.data.setdefault(key, [])
|
|
85
|
-
self.band_TxRx_homecall_report_times.data[key].append(tnow)
|
|
92
|
+
self.add_homespots_record((d['b'], iTxRx, call), tnow)
|
|
86
93
|
if d['sc'] == self.my_call:
|
|
87
|
-
self.hearing_me.data.setdefault(d['b'], {})
|
|
88
94
|
if d['rc'] not in self.hearing_me.data[d['b']]:
|
|
89
95
|
self.hearing_me_new.append(d['rc'])
|
|
90
|
-
self.hearing_me.data
|
|
96
|
+
self.add_myspots_record(self.hearing_me.data, d['b'], d['rc'], tnow, d['rp'])
|
|
91
97
|
if d['rc'] == self.my_call:
|
|
92
|
-
self.heard_by_me.data
|
|
98
|
+
self.add_myspots_record(self.heard_by_me.data, d['b'], d['sc'], tnow, d['rp'])
|
|
93
99
|
if d['sc'] not in self.heard_by_me.data[d['b']]:
|
|
94
100
|
self.heard_by_me_new.append(d['sc'])
|
|
95
101
|
self.heard_by_me.data[d['b']][d['sc']] = {'t': tnow,'rp': d['rp'],'c': d['sc']}
|
|
@@ -99,7 +105,6 @@ class PSKR_MQTT_listener:
|
|
|
99
105
|
while True:
|
|
100
106
|
time.sleep(5)
|
|
101
107
|
self.home_activity = {}
|
|
102
|
-
self.band_TxRx_homecall_couniTxRxemotes = {}
|
|
103
108
|
self.home_most_remotes = {}
|
|
104
109
|
with self.lock:
|
|
105
110
|
# clear counters for each band
|
|
@@ -108,10 +113,10 @@ class PSKR_MQTT_listener:
|
|
|
108
113
|
for b in self.home_most_remotes:
|
|
109
114
|
self.home_most_remotes[b] = [('',0), ('',0)]
|
|
110
115
|
|
|
111
|
-
# keep only the remote spots that happened in the self.
|
|
116
|
+
# keep only the remote spots that happened in the self.pskr_refresh_mins window
|
|
112
117
|
for band_TxRx_homecall in self.band_TxRx_homecall_report_times.data:
|
|
113
118
|
band_TxRx_homecall_report_times = self.band_TxRx_homecall_report_times.data[band_TxRx_homecall]
|
|
114
|
-
band_TxRx_homecall_report_times = [t for t in band_TxRx_homecall_report_times if (time.time() - t) < self.
|
|
119
|
+
band_TxRx_homecall_report_times = [t for t in band_TxRx_homecall_report_times if (time.time() - t) < 60*self.pskr_refresh_mins]
|
|
115
120
|
self.band_TxRx_homecall_report_times.data[band_TxRx_homecall] = band_TxRx_homecall_report_times
|
|
116
121
|
|
|
117
122
|
# count number of local Tx and Rx, and identify the local Tx and Rx with most remote spots
|
|
@@ -133,13 +138,3 @@ class PSKR_MQTT_listener:
|
|
|
133
138
|
n_spotted = len(rx_reports) if rx_reports else 0
|
|
134
139
|
return n_spotted, n_spotting
|
|
135
140
|
|
|
136
|
-
if __name__ == '__main__':
|
|
137
|
-
pskr = PSKR_MQTT_listener("IO90")
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
@@ -14,10 +14,11 @@ 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.7'
|
|
18
18
|
|
|
19
19
|
MAX_TX_START_SECONDS = 2.5
|
|
20
|
-
|
|
20
|
+
HEARING_PANEL_LIFE_MINS = 5
|
|
21
|
+
PSKR_REFRESH_MINS = 20
|
|
21
22
|
rig, gui, qso, adif_logging, pskr_info, pskr_upload = None, None, None, None, None, None
|
|
22
23
|
busy_profile, hearing_me = None, None
|
|
23
24
|
|
|
@@ -253,18 +254,25 @@ def write_all_txt_row(message):
|
|
|
253
254
|
|
|
254
255
|
#============= Callbacks for Receiver ==========================================================
|
|
255
256
|
def on_rx_decode(c):
|
|
257
|
+
if (c.decode_completed - qso.band_info['time_set']) < 5: # prevent bad QRG -> heard_by_me and pskreporter upload data
|
|
258
|
+
return
|
|
256
259
|
message = Message(c)
|
|
257
260
|
if gui:
|
|
258
261
|
gui.add_message_box(message)
|
|
259
|
-
if qso.band_info['b'] is not None and pskr_upload is not None:
|
|
260
|
-
_, dx_call, dx_grid = c.msg_tuple
|
|
261
|
-
if dx_call != 'not' and dx_call != config['station']['call']:
|
|
262
|
-
pskr_upload.add_report(dx_call, int(1000000*float(qso.band_info['fMHz'])) + c.fHz, c.snr, 'FT8', 1, int(time.time()))
|
|
263
|
-
loc = pskr_info.callsign_cache.data.get(dx_call, dx_grid)
|
|
264
|
-
# can't use the next line unless I keep more info than just time in the spots data and deduplicate
|
|
265
|
-
# pskr_info.add_spot({'sc':dx_call, 'sl':loc, 'rc':config['station']['call'], 'rl':config['station']['grid'], 'b':qso.band_info['b'], 'rp': c.snr})
|
|
266
262
|
print(message.wsjtx_screen_format())
|
|
267
263
|
write_all_txt_row(message)
|
|
264
|
+
if qso.band_info['b'] is not None and pskr_upload is not None:
|
|
265
|
+
call_a, call_b, grid_rpt = c.msg_tuple
|
|
266
|
+
if call_b == 'not':
|
|
267
|
+
return
|
|
268
|
+
call_b_grid = grid_rpt if isGrid(grid_rpt) else ''
|
|
269
|
+
if call_b != config['station']['call']:
|
|
270
|
+
pskr_upload.add_report(call_b, int(1000000*float(qso.band_info['fMHz'])) + c.fHz, c.snr, 'FT8', 1, int(time.time()))
|
|
271
|
+
pskr_info.store_best_location(call_b, call_b_grid)
|
|
272
|
+
pskr_info.add_myspots_record(pskr_info.heard_by_me.data, qso.band_info['b'], call_b, int(time.time()), c.snr)
|
|
273
|
+
if call_b == config['station']['call'] and (isReport(grid_rpt) or isRReport(grid_rpt)):
|
|
274
|
+
rpt = grid_rpt.replace("R","")
|
|
275
|
+
pskr_info.add_myspots_record(pskr_info.hearing_me.data, qso.band_info['b'], call_a, int(time.time()), rpt)
|
|
268
276
|
|
|
269
277
|
def on_rx_busy_profile(busy_profile_new, cycle):
|
|
270
278
|
global busy_profile, clearest_frequency
|
|
@@ -280,7 +288,7 @@ def on_rx_busy_profile(busy_profile_new, cycle):
|
|
|
280
288
|
console_print(f"[on_busy] Clear Tx frequency found at {clearest_frequency:6.1f}")
|
|
281
289
|
|
|
282
290
|
#============= Callbacks for GUI ==========================================================
|
|
283
|
-
def on_gui_sidebars_refresh(gui):
|
|
291
|
+
def on_gui_sidebars_refresh(gui, display_cycle):
|
|
284
292
|
if qso.band_info['b'] is None:
|
|
285
293
|
console_print(f"[PyFT8] Band not set; please select a band.", color = 'red')
|
|
286
294
|
if pskr_info is None:
|
|
@@ -310,18 +318,24 @@ def on_gui_sidebars_refresh(gui):
|
|
|
310
318
|
gui.band_stats.scroll_print(f"{n_spotted:<7} {rx_lead[1]:<7}", color = '#b6f0c6')
|
|
311
319
|
|
|
312
320
|
#refresh hearing me / heard by me panel
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
321
|
+
data = pskr_info.hearing_me.data if display_cycle == 1 else pskr_info.heard_by_me.data
|
|
322
|
+
timewindow_str = f"<{HEARING_PANEL_LIFE_MINS:.0f} mins"
|
|
323
|
+
title_txt = f"Hearing me {timewindow_str}" if display_cycle==1 else f"Heard by me {timewindow_str}"
|
|
324
|
+
display_rows = [(title_txt, 2e40, 'white')]
|
|
325
|
+
tnow = time.time()
|
|
317
326
|
if b is not None and b in data:
|
|
318
|
-
|
|
319
|
-
for
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
327
|
+
band_rpts = data[b]
|
|
328
|
+
calls_now = [call for call in band_rpts if (tnow - band_rpts[call]['t']) < 60*HEARING_PANEL_LIFE_MINS]
|
|
329
|
+
subtitle_txt = f"{len(calls_now)}/{len(band_rpts)} now/ever"
|
|
330
|
+
display_rows.append((subtitle_txt, 1e40, 'white'))
|
|
331
|
+
new_calls = pskr_info.hearing_me_new if display_cycle == 1 else pskr_info.heard_by_me_new
|
|
332
|
+
for remote_call in calls_now:
|
|
333
|
+
rpt = band_rpts[remote_call]
|
|
334
|
+
call, snr, geo_text, timestamp = rpt['c'], int(rpt['rp']), get_geo_text(remote_call), rpt['t']
|
|
335
|
+
color = 'white' if remote_call in new_calls else 'lime'
|
|
336
|
+
display_rows.append((f"{remote_call:<7} {snr:+03d} {geo_text:<12}", timestamp, color))
|
|
337
|
+
display_rows.sort(key = lambda row: row[1], reverse = True)
|
|
338
|
+
gui.hm.list_print([row[0] for row in display_rows], [row[2] for row in display_rows])
|
|
325
339
|
|
|
326
340
|
def on_gui_control_click(btn_def):
|
|
327
341
|
btn_action = btn_def['action']
|
|
@@ -336,7 +350,7 @@ def on_gui_control_click(btn_def):
|
|
|
336
350
|
qso.tx_cycle = None
|
|
337
351
|
if(btn_action == 'SET_BAND'):
|
|
338
352
|
band, freqMHz = btn_def['band'], btn_def['freq']
|
|
339
|
-
qso.band_info = {'b':band, 'fMHz':freqMHz}
|
|
353
|
+
qso.band_info = {'b':band, 'fMHz':freqMHz, 'time_set':time.time()}
|
|
340
354
|
rig.set_freq_Hz(int(1000000*float(qso.band_info['fMHz'])))
|
|
341
355
|
console_print(f"[PyFT8] Set band: {qso.band_info['b']} {qso.band_info['fMHz']}")
|
|
342
356
|
gui.band_stats.clear()
|
|
@@ -373,7 +387,7 @@ def cli():
|
|
|
373
387
|
if mc is not None and 'pskreporter' in config.keys():
|
|
374
388
|
if config['pskreporter']['upload'] == 'Y':
|
|
375
389
|
pskr_upload = PSKR_upload(mc, mg, software = f"PyFT8 v{VER}", console_print = console_print) if not mc is None else None
|
|
376
|
-
pskr_info = PSKR_MQTT_listener(config_folder, mc, mg[:4],
|
|
390
|
+
pskr_info = PSKR_MQTT_listener(config_folder, mc, mg[:4], PSKR_REFRESH_MINS)
|
|
377
391
|
qso = FT8_QSO()
|
|
378
392
|
if config.has_section('hamlib_rig'):
|
|
379
393
|
console_print("Connecting to rig via Hamlib")
|
|
@@ -405,7 +419,7 @@ def cli():
|
|
|
405
419
|
rx = Receiver(audio_in, [200, 3100], on_rx_decode, on_rx_busy_profile)
|
|
406
420
|
audio_in.start_streamed_audio(input_device_idx)
|
|
407
421
|
if gui is not None:
|
|
408
|
-
gui.set_bandstats_title(f"Spots
|
|
422
|
+
gui.set_bandstats_title(f"Pskreporter Spots\nto/from {config['station']['grid'][:4]} <{PSKR_REFRESH_MINS:.0f} mins")
|
|
409
423
|
gui.plt.show()
|
|
410
424
|
else:
|
|
411
425
|
wait_for_keyboard()
|
|
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
|
|
File without changes
|