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.
- {pyft8-2.12.2 → pyft8-2.12.3}/PKG-INFO +4 -8
- {pyft8-2.12.2 → pyft8-2.12.3}/PyFT8/databases.py +30 -21
- {pyft8-2.12.2 → pyft8-2.12.3}/PyFT8/pyft8.py +4 -3
- {pyft8-2.12.2 → pyft8-2.12.3}/PyFT8.egg-info/PKG-INFO +4 -8
- {pyft8-2.12.2 → pyft8-2.12.3}/README.md +3 -7
- {pyft8-2.12.2 → pyft8-2.12.3}/pyproject.toml +1 -1
- {pyft8-2.12.2 → pyft8-2.12.3}/LICENSE +0 -0
- {pyft8-2.12.2 → pyft8-2.12.3}/MANIFEST.in +0 -0
- {pyft8-2.12.2 → pyft8-2.12.3}/PyFT8/__init__.py +0 -0
- {pyft8-2.12.2 → pyft8-2.12.3}/PyFT8/gui.py +0 -0
- {pyft8-2.12.2 → pyft8-2.12.3}/PyFT8/pskreporter.py +0 -0
- {pyft8-2.12.2 → pyft8-2.12.3}/PyFT8/receiver.py +0 -0
- {pyft8-2.12.2 → pyft8-2.12.3}/PyFT8/rigctrl.py +0 -0
- {pyft8-2.12.2 → pyft8-2.12.3}/PyFT8/time_utils.py +0 -0
- {pyft8-2.12.2 → pyft8-2.12.3}/PyFT8/transmitter.py +0 -0
- {pyft8-2.12.2 → pyft8-2.12.3}/PyFT8.egg-info/SOURCES.txt +0 -0
- {pyft8-2.12.2 → pyft8-2.12.3}/PyFT8.egg-info/dependency_links.txt +0 -0
- {pyft8-2.12.2 → pyft8-2.12.3}/PyFT8.egg-info/entry_points.txt +0 -0
- {pyft8-2.12.2 → pyft8-2.12.3}/PyFT8.egg-info/requires.txt +0 -0
- {pyft8-2.12.2 → pyft8-2.12.3}/PyFT8.egg-info/top_level.txt +0 -0
- {pyft8-2.12.2 → pyft8-2.12.3}/setup.cfg +0 -0
- {pyft8-2.12.2 → pyft8-2.12.3}/tests/dev/CQ AAAA.py +0 -0
- {pyft8-2.12.2 → pyft8-2.12.3}/tests/dev/osd.py +0 -0
- {pyft8-2.12.2 → pyft8-2.12.3}/tests/dev/test_generate_wav.py +0 -0
- {pyft8-2.12.2 → pyft8-2.12.3}/tests/dev/test_loopback_performance.py +0 -0
- {pyft8-2.12.2 → pyft8-2.12.3}/tests/dev/view_worked_before.py +0 -0
- {pyft8-2.12.2 → pyft8-2.12.3}/tests/plot_baseline.py +0 -0
- {pyft8-2.12.2 → pyft8-2.12.3}/tests/spare.py +0 -0
- {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.
|
|
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
|
-
|
|
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
|
-

|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
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
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
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.
|
|
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
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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
|
-

|
|
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
|
-
|
|
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
|
-
|
|
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
|
-

|
|
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
|
-
|
|
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.
|
|
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
|