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.
Files changed (32) hide show
  1. {pyft8-2.7.2 → pyft8-2.7.4}/PKG-INFO +1 -1
  2. {pyft8-2.7.2 → pyft8-2.7.4}/PyFT8/gui.py +7 -7
  3. {pyft8-2.7.2 → pyft8-2.7.4}/PyFT8/mqtt.py +27 -18
  4. {pyft8-2.7.2 → pyft8-2.7.4}/PyFT8/pyft8.py +15 -9
  5. {pyft8-2.7.2 → pyft8-2.7.4}/PyFT8/time_utils.py +1 -1
  6. {pyft8-2.7.2 → pyft8-2.7.4}/PyFT8.egg-info/PKG-INFO +1 -1
  7. {pyft8-2.7.2 → pyft8-2.7.4}/pyproject.toml +1 -1
  8. {pyft8-2.7.2 → pyft8-2.7.4}/LICENSE +0 -0
  9. {pyft8-2.7.2 → pyft8-2.7.4}/MANIFEST.in +0 -0
  10. {pyft8-2.7.2 → pyft8-2.7.4}/PyFT8/__init__.py +0 -0
  11. {pyft8-2.7.2 → pyft8-2.7.4}/PyFT8/callhashes.py +0 -0
  12. {pyft8-2.7.2 → pyft8-2.7.4}/PyFT8/hamlib.py +0 -0
  13. {pyft8-2.7.2 → pyft8-2.7.4}/PyFT8/maidenhead.py +0 -0
  14. {pyft8-2.7.2 → pyft8-2.7.4}/PyFT8/pskr_upload.py +0 -0
  15. {pyft8-2.7.2 → pyft8-2.7.4}/PyFT8/receiver.py +0 -0
  16. {pyft8-2.7.2 → pyft8-2.7.4}/PyFT8/rigctrl.py +0 -0
  17. {pyft8-2.7.2 → pyft8-2.7.4}/PyFT8/transmitter.py +0 -0
  18. {pyft8-2.7.2 → pyft8-2.7.4}/PyFT8.egg-info/SOURCES.txt +0 -0
  19. {pyft8-2.7.2 → pyft8-2.7.4}/PyFT8.egg-info/dependency_links.txt +0 -0
  20. {pyft8-2.7.2 → pyft8-2.7.4}/PyFT8.egg-info/entry_points.txt +0 -0
  21. {pyft8-2.7.2 → pyft8-2.7.4}/PyFT8.egg-info/requires.txt +0 -0
  22. {pyft8-2.7.2 → pyft8-2.7.4}/PyFT8.egg-info/top_level.txt +0 -0
  23. {pyft8-2.7.2 → pyft8-2.7.4}/README.md +0 -0
  24. {pyft8-2.7.2 → pyft8-2.7.4}/setup.cfg +0 -0
  25. {pyft8-2.7.2 → pyft8-2.7.4}/tests/dev/CQ AAAA.py +0 -0
  26. {pyft8-2.7.2 → pyft8-2.7.4}/tests/dev/osd.py +0 -0
  27. {pyft8-2.7.2 → pyft8-2.7.4}/tests/dev/test_generate_wav.py +0 -0
  28. {pyft8-2.7.2 → pyft8-2.7.4}/tests/dev/test_loopback_performance.py +0 -0
  29. {pyft8-2.7.2 → pyft8-2.7.4}/tests/dev/view_worked_before.py +0 -0
  30. {pyft8-2.7.2 → pyft8-2.7.4}/tests/plot_baseline.py +0 -0
  31. {pyft8-2.7.2 → pyft8-2.7.4}/tests/spare.py +0 -0
  32. {pyft8-2.7.2 → pyft8-2.7.4}/tests/test_batch_and_live.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: PyFT8
3
- Version: 2.7.2
3
+ Version: 2.7.4
4
4
  Summary: FT8 Decoding and Encoding in Python with test/loopback code
5
5
  Author-email: G1OJS <g1ojs@yahoo.com>
6
6
  License-Expression: GPL-3.0-or-later
@@ -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
- self.lines = [{'text':l, 'color':'white'} for l in lst[:self.nlines]]
55
- for i, line in enumerate(self.lines):
56
- if line['text'] != self.lineartists[i].get_text():
57
- self.lineartists[i].set_text(line['text'])
58
- self.lineartists[i].set_color(line['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
- if(os.path.exists(self.file)):
26
- with open(f"{self.file}","rb") as f:
27
- self.data = pickle.load(f)
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
- with open(f"{self.file}","wb") as f:
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}/report_times_271.pkl")
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(time.time())
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'], {})[d['rc']] = {'t': time.time(),'rp': d['rp'],'c': d['rc']}
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.2'
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
- #console_print(f"[on_busy] Set Tx freq to {clearest_frequency:6.1f}")
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
- hearing_me_text = []
311
- if b is not None and b in pskr_info.hearing_me.data:
312
- hm = pskr_info.hearing_me.data[b].values()
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
- hearing_me_text.append(f"{h['c']:<7} {int(h['rp']):+03d} {geo_text:<12}")
316
- gui.hm.list_print(['Hearing me:'] + hearing_me_text)
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)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: PyFT8
3
- Version: 2.7.2
3
+ Version: 2.7.4
4
4
  Summary: FT8 Decoding and Encoding in Python with test/loopback code
5
5
  Author-email: G1OJS <g1ojs@yahoo.com>
6
6
  License-Expression: GPL-3.0-or-later
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "PyFT8"
3
- version = "2.7.2"
3
+ version = "2.7.4"
4
4
  license = "GPL-3.0-or-later"
5
5
 
6
6
  authors = [
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