PyFT8 2.12.2__tar.gz → 2.12.3__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 (29) hide show
  1. {pyft8-2.12.2 → pyft8-2.12.3}/PKG-INFO +4 -8
  2. {pyft8-2.12.2 → pyft8-2.12.3}/PyFT8/databases.py +30 -21
  3. {pyft8-2.12.2 → pyft8-2.12.3}/PyFT8/pyft8.py +4 -3
  4. {pyft8-2.12.2 → pyft8-2.12.3}/PyFT8.egg-info/PKG-INFO +4 -8
  5. {pyft8-2.12.2 → pyft8-2.12.3}/README.md +3 -7
  6. {pyft8-2.12.2 → pyft8-2.12.3}/pyproject.toml +1 -1
  7. {pyft8-2.12.2 → pyft8-2.12.3}/LICENSE +0 -0
  8. {pyft8-2.12.2 → pyft8-2.12.3}/MANIFEST.in +0 -0
  9. {pyft8-2.12.2 → pyft8-2.12.3}/PyFT8/__init__.py +0 -0
  10. {pyft8-2.12.2 → pyft8-2.12.3}/PyFT8/gui.py +0 -0
  11. {pyft8-2.12.2 → pyft8-2.12.3}/PyFT8/pskreporter.py +0 -0
  12. {pyft8-2.12.2 → pyft8-2.12.3}/PyFT8/receiver.py +0 -0
  13. {pyft8-2.12.2 → pyft8-2.12.3}/PyFT8/rigctrl.py +0 -0
  14. {pyft8-2.12.2 → pyft8-2.12.3}/PyFT8/time_utils.py +0 -0
  15. {pyft8-2.12.2 → pyft8-2.12.3}/PyFT8/transmitter.py +0 -0
  16. {pyft8-2.12.2 → pyft8-2.12.3}/PyFT8.egg-info/SOURCES.txt +0 -0
  17. {pyft8-2.12.2 → pyft8-2.12.3}/PyFT8.egg-info/dependency_links.txt +0 -0
  18. {pyft8-2.12.2 → pyft8-2.12.3}/PyFT8.egg-info/entry_points.txt +0 -0
  19. {pyft8-2.12.2 → pyft8-2.12.3}/PyFT8.egg-info/requires.txt +0 -0
  20. {pyft8-2.12.2 → pyft8-2.12.3}/PyFT8.egg-info/top_level.txt +0 -0
  21. {pyft8-2.12.2 → pyft8-2.12.3}/setup.cfg +0 -0
  22. {pyft8-2.12.2 → pyft8-2.12.3}/tests/dev/CQ AAAA.py +0 -0
  23. {pyft8-2.12.2 → pyft8-2.12.3}/tests/dev/osd.py +0 -0
  24. {pyft8-2.12.2 → pyft8-2.12.3}/tests/dev/test_generate_wav.py +0 -0
  25. {pyft8-2.12.2 → pyft8-2.12.3}/tests/dev/test_loopback_performance.py +0 -0
  26. {pyft8-2.12.2 → pyft8-2.12.3}/tests/dev/view_worked_before.py +0 -0
  27. {pyft8-2.12.2 → pyft8-2.12.3}/tests/plot_baseline.py +0 -0
  28. {pyft8-2.12.2 → pyft8-2.12.3}/tests/spare.py +0 -0
  29. {pyft8-2.12.2 → pyft8-2.12.3}/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.12.2
3
+ Version: 2.12.3
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
@@ -48,13 +48,8 @@ If you're interested in how this works, maybe have a look at [MiniPyFT8](https:/
48
48
  - Number of remote stations hearing your Tx, number of remote Txs that you're hearing, plus the same info for the 'best' station in your level 4 square
49
49
  - Data used for the above is cached to disk so is not lost when restarting the program
50
50
 
51
- To enable uploading of spots to pskreporter, make sure that your .ini file includes
52
- ```
53
- [pskreporter]
54
- upload = Y
55
- ```
51
+ <img width="997" height="1025" alt="screenshot" src="https://github.com/user-attachments/assets/88228f65-c39e-45ee-9f73-1d85138563f3" />
56
52
 
57
- ![screenshot](screenshot.png)
58
53
 
59
54
  ## Motivation
60
55
  This started out as me thinking "How hard can it be, really?" after some frustration with Windows moving sound devices around and wanting to get a minimal decoder running that I can fully control.
@@ -128,7 +123,8 @@ upload = Y
128
123
 
129
124
  The image below shows the number of decodes from PyFT8, WSJT-x V2.7.0 running in NORM mode, and FT8_lib, using the same 10 minutes of busy 20m audio that is used to test ft8_lib.
130
125
 
131
- ![performance snapshot](performance%20snapshot.png)
126
+ <img width="640" height="480" alt="performance snapshot" src="https://github.com/user-attachments/assets/b62221ec-2d74-4fbc-9c61-34b2bbc0e963" />
127
+
132
128
 
133
129
  ## Limitations
134
130
  PyFT8 doesn't decode / encode *all* message types. The table below shows which are handled.
@@ -2,6 +2,7 @@ from PyFT8.pskreporter import PSKR_MQTT_listener
2
2
  import threading, time, os, pickle, json
3
3
 
4
4
  call_hashes = {}
5
+
5
6
  def add_call_hashes(call):
6
7
  global call_hashes
7
8
  chars = " 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ/"
@@ -64,16 +65,18 @@ class DiskDict:
64
65
  with self.lock:
65
66
  if(os.path.exists(self.file)):
66
67
  with open(f"{self.file}","rb") as f:
67
- self.data = json.load(f)
68
+ try:
69
+ self.data = json.load(f)
70
+ except:
71
+ pass
68
72
 
69
73
  def save(self):
70
74
  with self.lock:
71
- tmp_file = f"{self.file}.tmp"
72
- with open(tmp_file, "w") as f:
73
- json.dump(self.data, f)
74
- f.flush()
75
- os.fsync(f.fileno())
76
- os.replace(tmp_file, self.file)
75
+ with open(self.file, "w") as f:
76
+ try:
77
+ json.dump(self.data, f)
78
+ except:
79
+ pass
77
80
 
78
81
  class History:
79
82
  def __init__(self, config_folder, my_call, home_square, pskr_refresh_mins, parse_all_file):
@@ -83,6 +86,7 @@ class History:
83
86
  self.my_call = my_call
84
87
  self.home_square = home_square
85
88
  self.home_square_lev4 = home_square[:4]
89
+ self.freqs_to_bands = {}
86
90
  self.dist_brg_cache = {}
87
91
  self.hearing_me = DiskDict(f"{self.config_folder}/hearing_me.json", 3) # all-time record of hearing me
88
92
  self.heard_by_me = DiskDict(f"{self.config_folder}/heard_by_me.json", 5) # all-time record of heard by me
@@ -102,29 +106,29 @@ class History:
102
106
  mqtt = PSKR_MQTT_listener(self.home_square_lev4, self.add_mqtt_spot)
103
107
  threading.Thread(target = self.count_activity, daemon = True).start()
104
108
 
105
- def band_from_MHz(self, fMHz): # rewrite this to use the band button defs from ini file
106
- f = int(fMHz)
107
- if f > 0:
108
- freqs = [1,3,5,7,10,14,18,21,24,28,50,144,433]
109
- idx = freqs.index(f) if f in freqs else -1
110
- if idx > -1:
111
- return ['160m','80m','60m','40m','30m','20m','17m','15m','12m','10m','6m','2m','70cm'][idx]
112
-
113
- def load_from_wb(self, log_cache):
109
+ def set_bands(self, bands):
110
+ for b in bands:
111
+ f = float(bands[b])
112
+ self.freqs_to_bands[round(f,1)] = b
113
+
114
+ def load_hearing_heard_from_adif(self, log_cache):
114
115
  for key in log_cache:
115
116
  key_parts = key.split('_')
116
117
  if len(key_parts) > 1:
117
118
  c, b, m = key_parts
118
119
  if m == 'FT8':
120
+ #print(f"Parsing adif log: Add hearing & heard by {c} on {b}")
119
121
  self.add_myspots_record(self.hearing_me.data, None, b, c, 0, 0)
120
122
  self.add_myspots_record(self.heard_by_me.data, None, b, c, 0, 0)
123
+ else:
124
+ print(m)
121
125
 
122
126
  def load_all_file(self, all_file):
123
127
  recs = self.parse_all_txt(all_file)
124
128
  if not any(recs): return
125
129
  for r in recs:
126
130
  if r['md'] == 'FT8':
127
- band = self.band_from_MHz(r['fMHz'])
131
+ band = self.freqs_to_bands.get(round(r['fMHz'], 1), None)
128
132
  if band is not None:
129
133
  TxRx = 'Tx' if (r['TxRx'] == 'Tx' or r['call_b'] == self.my_call) else 'Rx'
130
134
  call = r['call_b'] if TxRx == 'Rx' else r['call_a']
@@ -298,6 +302,10 @@ class ADIF:
298
302
  import calendar
299
303
  def parse(rec, field):
300
304
  p = rec.find(field)
305
+ if p<0:
306
+ p = rec.find(field.upper())
307
+ if p<0:
308
+ p = rec.find(field.lower())
301
309
  if p > 0:
302
310
  p1, p2 = rec.find(':',p), rec.find('>',p)
303
311
  n = int(rec[p1+1:p2])
@@ -307,8 +315,9 @@ class ADIF:
307
315
  for l in f.readlines():
308
316
  if parse(l, 'mode') == "FT8":
309
317
  c, b, d, t = parse(l, 'call'), parse(l, 'band'), parse(l, 'qso_date'), parse(l, 'time_on')
310
- time_tuple = time.strptime(d+t, "%Y%m%d%H%M%S")
311
- tm = calendar.timegm(time_tuple)
312
- cache[c] = tm
313
- cache[c + "_"+b+"_FT8"] = tm
318
+ if c and b and d and t:
319
+ time_tuple = time.strptime(d+t, "%Y%m%d%H%M%S")
320
+ tm = calendar.timegm(time_tuple)
321
+ cache[c] = tm
322
+ cache[c + "_"+b+"_FT8"] = tm
314
323
  return cache
@@ -12,7 +12,7 @@ from PyFT8.time_utils import global_time_utils
12
12
  from PyFT8.rigctrl import Rig_CAT, Rig_hamlib
13
13
  from PyFT8.databases import History, ADIF
14
14
 
15
- VER = '2.12.2'
15
+ VER = '2.12.3'
16
16
 
17
17
  MAX_TX_START_SECONDS = 2.5
18
18
  HEARING_PANEL_LIFE_MINS = 5
@@ -57,7 +57,7 @@ class Message:
57
57
  mycall = qso.mStation['c']
58
58
  self.h0_idx, self.f0_idx, self.msg_tuple, self.msg, self.snr, self.dt, self.fHz = c.h0_idx, c.f0_idx, c.msg_tuple, c.msg, c.snr, c.dt, c.fHz
59
59
  self.cyclestart = c.cyclestart
60
- self.expire = time.time() + 29.8
60
+ self.expire = time.time() + 29.25
61
61
  self.is_from_me = c.msg_tuple[1] == mycall
62
62
  self.is_to_me = c.msg_tuple[0] == mycall
63
63
  self.is_cq = c.msg_tuple[0].startswith('CQ')
@@ -378,8 +378,9 @@ def cli():
378
378
  if not args.no_gui:
379
379
  gui = Gui(audio_in.dBgrid_main, 4, 2, config, on_gui_sidebars_refresh, on_gui_msg_click, on_gui_control_click)
380
380
  history = History(config_folder, mc, mg, PSKR_REFRESH_MINS, args.parse_all_file)
381
+ history.set_bands(config['bands'])
381
382
  adif_logging = ADIF(f"{config_folder}/PyFT8.adi")
382
- history.load_from_wb(adif_logging.cache)
383
+ history.load_hearing_heard_from_adif(adif_logging.cache)
383
384
 
384
385
  if mc is not None and 'pskreporter' in config.keys():
385
386
  if config['pskreporter']['upload'] == 'Y':
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: PyFT8
3
- Version: 2.12.2
3
+ Version: 2.12.3
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
@@ -48,13 +48,8 @@ If you're interested in how this works, maybe have a look at [MiniPyFT8](https:/
48
48
  - Number of remote stations hearing your Tx, number of remote Txs that you're hearing, plus the same info for the 'best' station in your level 4 square
49
49
  - Data used for the above is cached to disk so is not lost when restarting the program
50
50
 
51
- To enable uploading of spots to pskreporter, make sure that your .ini file includes
52
- ```
53
- [pskreporter]
54
- upload = Y
55
- ```
51
+ <img width="997" height="1025" alt="screenshot" src="https://github.com/user-attachments/assets/88228f65-c39e-45ee-9f73-1d85138563f3" />
56
52
 
57
- ![screenshot](screenshot.png)
58
53
 
59
54
  ## Motivation
60
55
  This started out as me thinking "How hard can it be, really?" after some frustration with Windows moving sound devices around and wanting to get a minimal decoder running that I can fully control.
@@ -128,7 +123,8 @@ upload = Y
128
123
 
129
124
  The image below shows the number of decodes from PyFT8, WSJT-x V2.7.0 running in NORM mode, and FT8_lib, using the same 10 minutes of busy 20m audio that is used to test ft8_lib.
130
125
 
131
- ![performance snapshot](performance%20snapshot.png)
126
+ <img width="640" height="480" alt="performance snapshot" src="https://github.com/user-attachments/assets/b62221ec-2d74-4fbc-9c61-34b2bbc0e963" />
127
+
132
128
 
133
129
  ## Limitations
134
130
  PyFT8 doesn't decode / encode *all* message types. The table below shows which are handled.
@@ -29,13 +29,8 @@ If you're interested in how this works, maybe have a look at [MiniPyFT8](https:/
29
29
  - Number of remote stations hearing your Tx, number of remote Txs that you're hearing, plus the same info for the 'best' station in your level 4 square
30
30
  - Data used for the above is cached to disk so is not lost when restarting the program
31
31
 
32
- To enable uploading of spots to pskreporter, make sure that your .ini file includes
33
- ```
34
- [pskreporter]
35
- upload = Y
36
- ```
32
+ <img width="997" height="1025" alt="screenshot" src="https://github.com/user-attachments/assets/88228f65-c39e-45ee-9f73-1d85138563f3" />
37
33
 
38
- ![screenshot](screenshot.png)
39
34
 
40
35
  ## Motivation
41
36
  This started out as me thinking "How hard can it be, really?" after some frustration with Windows moving sound devices around and wanting to get a minimal decoder running that I can fully control.
@@ -109,7 +104,8 @@ upload = Y
109
104
 
110
105
  The image below shows the number of decodes from PyFT8, WSJT-x V2.7.0 running in NORM mode, and FT8_lib, using the same 10 minutes of busy 20m audio that is used to test ft8_lib.
111
106
 
112
- ![performance snapshot](performance%20snapshot.png)
107
+ <img width="640" height="480" alt="performance snapshot" src="https://github.com/user-attachments/assets/b62221ec-2d74-4fbc-9c61-34b2bbc0e963" />
108
+
113
109
 
114
110
  ## Limitations
115
111
  PyFT8 doesn't decode / encode *all* message types. The table below shows which are handled.
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "PyFT8"
3
- version = "2.12.2"
3
+ version = "2.12.3"
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