PyFT8 3.6.4__tar.gz → 3.6.6__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-3.6.4 → pyft8-3.6.6}/PKG-INFO +1 -1
- {pyft8-3.6.4 → pyft8-3.6.6}/PyFT8/gui.py +56 -19
- {pyft8-3.6.4 → pyft8-3.6.6}/PyFT8/message_broker.py +10 -7
- {pyft8-3.6.4 → pyft8-3.6.6}/PyFT8/pyft8.py +1 -1
- {pyft8-3.6.4 → pyft8-3.6.6}/PyFT8.egg-info/PKG-INFO +1 -1
- {pyft8-3.6.4 → pyft8-3.6.6}/pyproject.toml +1 -1
- pyft8-3.6.6/tests/spare2.py +37 -0
- pyft8-3.6.4/tests/spare2.py +0 -28
- {pyft8-3.6.4 → pyft8-3.6.6}/LICENSE +0 -0
- {pyft8-3.6.4 → pyft8-3.6.6}/MANIFEST.in +0 -0
- {pyft8-3.6.4 → pyft8-3.6.6}/PyFT8/__init__.py +0 -0
- {pyft8-3.6.4 → pyft8-3.6.6}/PyFT8/databases.py +0 -0
- {pyft8-3.6.4 → pyft8-3.6.6}/PyFT8/osd.py +0 -0
- {pyft8-3.6.4 → pyft8-3.6.6}/PyFT8/pskreporter.py +0 -0
- {pyft8-3.6.4 → pyft8-3.6.6}/PyFT8/qso_manager.py +0 -0
- {pyft8-3.6.4 → pyft8-3.6.6}/PyFT8/receiver.py +0 -0
- {pyft8-3.6.4 → pyft8-3.6.6}/PyFT8/rigctrl.py +0 -0
- {pyft8-3.6.4 → pyft8-3.6.6}/PyFT8/time_utils.py +0 -0
- {pyft8-3.6.4 → pyft8-3.6.6}/PyFT8/transmitter.py +0 -0
- {pyft8-3.6.4 → pyft8-3.6.6}/PyFT8.egg-info/SOURCES.txt +0 -0
- {pyft8-3.6.4 → pyft8-3.6.6}/PyFT8.egg-info/dependency_links.txt +0 -0
- {pyft8-3.6.4 → pyft8-3.6.6}/PyFT8.egg-info/entry_points.txt +0 -0
- {pyft8-3.6.4 → pyft8-3.6.6}/PyFT8.egg-info/requires.txt +0 -0
- {pyft8-3.6.4 → pyft8-3.6.6}/PyFT8.egg-info/top_level.txt +0 -0
- {pyft8-3.6.4 → pyft8-3.6.6}/README.md +0 -0
- {pyft8-3.6.4 → pyft8-3.6.6}/setup.cfg +0 -0
- {pyft8-3.6.4 → pyft8-3.6.6}/tests/misc/CQ AAAA.py +0 -0
- {pyft8-3.6.4 → pyft8-3.6.6}/tests/misc/animation.py +0 -0
- {pyft8-3.6.4 → pyft8-3.6.6}/tests/misc/bits_loopback.py +0 -0
- {pyft8-3.6.4 → pyft8-3.6.6}/tests/misc/bits_loopback_short.py +0 -0
- {pyft8-3.6.4 → pyft8-3.6.6}/tests/misc/osd.py +0 -0
- {pyft8-3.6.4 → pyft8-3.6.6}/tests/misc/symbols_loopback.py +0 -0
- {pyft8-3.6.4 → pyft8-3.6.6}/tests/misc/test_generate_wav.py +0 -0
- {pyft8-3.6.4 → pyft8-3.6.6}/tests/misc/test_loopback_performance.py +0 -0
- {pyft8-3.6.4 → pyft8-3.6.6}/tests/misc/text.py +0 -0
- {pyft8-3.6.4 → pyft8-3.6.6}/tests/misc/view_worked_before.py +0 -0
- {pyft8-3.6.4 → pyft8-3.6.6}/tests/pipeline/Fine tuning compare IOS_PC.py +0 -0
- {pyft8-3.6.4 → pyft8-3.6.6}/tests/pipeline/Targetted tests.py +0 -0
- {pyft8-3.6.4 → pyft8-3.6.6}/tests/pipeline/test_01.wav +0 -0
- {pyft8-3.6.4 → pyft8-3.6.6}/tests/play_wavs.py +0 -0
- {pyft8-3.6.4 → pyft8-3.6.6}/tests/plot_decode_times.py +0 -0
- {pyft8-3.6.4 → pyft8-3.6.6}/tests/spare.py +0 -0
- {pyft8-3.6.4 → pyft8-3.6.6}/tests/test_batch_and_live.py +0 -0
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import numpy as np
|
|
2
2
|
import matplotlib.pyplot as plt
|
|
3
3
|
from matplotlib import rcParams
|
|
4
|
+
import matplotlib.style as mplstyle
|
|
5
|
+
mplstyle.use('fast')
|
|
4
6
|
import queue
|
|
5
7
|
from matplotlib.widgets import Button
|
|
6
8
|
from PyFT8.time_utils import time_utils
|
|
@@ -30,17 +32,26 @@ class Msg_box:
|
|
|
30
32
|
from matplotlib.patches import Rectangle
|
|
31
33
|
self.fig, self.ax = fig, ax
|
|
32
34
|
message_type_params = MESSAGE_TYPES[message['message_type']]
|
|
33
|
-
rect = Rectangle((
|
|
35
|
+
rect = Rectangle((0, 0), w, h, edgecolor='lime', lw=2)
|
|
34
36
|
self.patch = self.ax.add_patch(rect)
|
|
35
37
|
self.patch.set_visible(False)
|
|
36
|
-
text_inst = self.ax.text(
|
|
37
|
-
text_inst.set_visible(False)
|
|
38
|
-
text_inst.
|
|
39
|
-
self.patch.
|
|
40
|
-
self.
|
|
38
|
+
self.text_inst = self.ax.text(0, 0, '', fontweight = 'bold' )
|
|
39
|
+
self.text_inst.set_visible(False)
|
|
40
|
+
self.text_inst.set_fontfamily('monospace')
|
|
41
|
+
self.artists = [self.patch, self.text_inst]
|
|
42
|
+
self.set_props(x, y, message)
|
|
43
|
+
|
|
44
|
+
def set_props(self, x, y, message):
|
|
45
|
+
self.xy = (x, y)
|
|
41
46
|
self.message = message
|
|
42
47
|
self.cycle = message['their_tx_cycle']
|
|
43
|
-
|
|
48
|
+
message_type_params = MESSAGE_TYPES[message['message_type']]
|
|
49
|
+
self.text_inst.set_text(message['display_text'])
|
|
50
|
+
self.text_inst.set_color(message_type_params['fg'])
|
|
51
|
+
self.patch.set_facecolor(message_type_params['bg'])
|
|
52
|
+
self.patch.set_alpha(message_type_params['alpha'])
|
|
53
|
+
self.text_inst.set_position((x, y+1))
|
|
54
|
+
self.patch.set_xy((x, y))
|
|
44
55
|
for a in self.artists:
|
|
45
56
|
a.set_visible(True)
|
|
46
57
|
|
|
@@ -168,32 +179,58 @@ class Gui:
|
|
|
168
179
|
def main_loop(self):
|
|
169
180
|
last_ptr = 0
|
|
170
181
|
self.plt.show(block = False)
|
|
182
|
+
target_update_time = 0.25
|
|
171
183
|
while True:
|
|
184
|
+
tstart_wf_redraw = time_utils.time()
|
|
172
185
|
self.image.set_data(self.waterfall_data['data'])
|
|
173
186
|
if self.needs_redraw:
|
|
174
187
|
self.needs_redraw = False
|
|
175
188
|
plt.pause(0.01)
|
|
176
189
|
else:
|
|
177
190
|
self.ax_wf.draw_artist(self.image)
|
|
191
|
+
t = time_utils.time()
|
|
178
192
|
for mb in self.msg_boxes:
|
|
179
193
|
mb.draw()
|
|
180
|
-
self.
|
|
194
|
+
#print(f"Drew {len(self.msg_boxes)} in {time_utils.time() - t:6.2f}s")
|
|
195
|
+
self.fig.canvas.blit(self.ax_wf.bbox)
|
|
196
|
+
#self.fig.canvas.update()
|
|
181
197
|
self.fig.canvas.flush_events()
|
|
198
|
+
#print(f"Updated at {time_utils.cycle_time():6.2f}s")
|
|
199
|
+
tdelay = target_update_time - (time_utils.time() - tstart_wf_redraw)
|
|
200
|
+
#print(tdelay)
|
|
201
|
+
if tdelay > 0.01:
|
|
202
|
+
time_utils.sleep(tdelay)
|
|
182
203
|
|
|
183
204
|
def before_search(self, curr_cycle):
|
|
184
|
-
self._hide_msg_boxes(curr_cycle)
|
|
205
|
+
self._hide_msg_boxes(curr_cycle) # hide boxes overlapping new decodes
|
|
206
|
+
self._hide_msg_boxes(curr_cycle = None) # hide all boxes
|
|
185
207
|
|
|
186
|
-
def after_search(self,curr_cycle):
|
|
208
|
+
def after_search(self, curr_cycle):
|
|
187
209
|
self._refresh_hearing()
|
|
188
210
|
self._refresh_band_buttons()
|
|
189
211
|
self._refresh_home_panel()
|
|
190
|
-
self._clear_msg_boxes(curr_cycle)
|
|
212
|
+
#self._clear_msg_boxes(curr_cycle)
|
|
191
213
|
|
|
192
214
|
def display_message(self, message):
|
|
193
215
|
x = int(message['t0'] / self.waterfall_data['dt'] + message['their_tx_cycle'] * self.waterfall_data['pixels_per_cycle'])
|
|
194
216
|
y = int(message['fHz'] / self.waterfall_data['df'])
|
|
195
|
-
mb =
|
|
196
|
-
self.msg_boxes
|
|
217
|
+
mb = None
|
|
218
|
+
for mb1 in self.msg_boxes:
|
|
219
|
+
if not mb1.patch.get_visible():
|
|
220
|
+
mb1.set_props(x, y, message)
|
|
221
|
+
mb = mb1
|
|
222
|
+
break
|
|
223
|
+
if not mb:
|
|
224
|
+
mb = Msg_box(self.fig, self.ax_wf, x, y, self.waterfall_data['sig_w'], self.waterfall_data['sig_h'], message)
|
|
225
|
+
self.msg_boxes.append(mb)
|
|
226
|
+
#print(f"{len(self.msg_boxes)} message boxes")
|
|
227
|
+
|
|
228
|
+
def update_message(self, display_text, update_dict):
|
|
229
|
+
for mb in self.msg_boxes:
|
|
230
|
+
if mb.patch.get_visible():
|
|
231
|
+
if mb.message['display_text'] == display_text:
|
|
232
|
+
mb.message.update(update_dict)
|
|
233
|
+
mb.set_props(mb.xy[0], mb.xy[1], mb.message)
|
|
197
234
|
|
|
198
235
|
def get_band_info(self):
|
|
199
236
|
return self.band_info
|
|
@@ -219,10 +256,9 @@ class Gui:
|
|
|
219
256
|
if band in self.configured_bands:
|
|
220
257
|
self.band_info = {'current_band':band, 'fMHz':self.configured_bands[band], 'time_set':time_utils.time()}
|
|
221
258
|
self.console_print(f"[PyFT8] Set band: {self.band_info['current_band']} {self.band_info['fMHz']}")
|
|
259
|
+
self._clear_msg_boxes()
|
|
222
260
|
|
|
223
261
|
def _oncanvasclick(self, clickargs):
|
|
224
|
-
#print(f"Click at {time_utils.cycle_time():6.2f}")
|
|
225
|
-
|
|
226
262
|
if clickargs.inaxes is self.ax_wf:
|
|
227
263
|
for mb in self.msg_boxes:
|
|
228
264
|
if mb.contains(clickargs.x, clickargs.y):
|
|
@@ -240,16 +276,17 @@ class Gui:
|
|
|
240
276
|
action = ["CQ", "RPT_LAST", "TX_OFF"][['CQ', 'Repeat last', 'Tx off'].index(bb.id)]
|
|
241
277
|
self.qso_manager.on_click({'action':action})
|
|
242
278
|
|
|
243
|
-
def _hide_msg_boxes(self, curr_cycle):
|
|
244
|
-
to_hide = [mb for mb in self.msg_boxes if mb.cycle == curr_cycle]
|
|
279
|
+
def _hide_msg_boxes(self, curr_cycle = None):
|
|
280
|
+
to_hide = [mb for mb in self.msg_boxes if (mb.cycle == curr_cycle) or (curr_cycle is None)]
|
|
245
281
|
for mb in to_hide:
|
|
246
282
|
mb.hide()
|
|
247
283
|
self.needs_redraw = True
|
|
248
284
|
|
|
249
285
|
def _clear_msg_boxes(self, curr_cycle = None):
|
|
250
|
-
to_remove = [mb for mb in self.msg_boxes if mb.cycle == curr_cycle or curr_cycle is None]
|
|
251
|
-
self.msg_boxes = [mb for mb in self.msg_boxes if mb.cycle != curr_cycle]
|
|
286
|
+
to_remove = [mb for mb in self.msg_boxes if (mb.cycle == curr_cycle) or (curr_cycle is None)]
|
|
287
|
+
self.msg_boxes = [mb for mb in self.msg_boxes if (mb.cycle != curr_cycle) and not (curr_cycle is None)]
|
|
252
288
|
for mb in to_remove:
|
|
289
|
+
mb.hide()
|
|
253
290
|
mb.remove()
|
|
254
291
|
self.needs_redraw = True
|
|
255
292
|
|
|
@@ -26,15 +26,10 @@ class Broker():
|
|
|
26
26
|
mtype = ['generic', 'from_me', 'to_me', 'CQ'][mtype_val]
|
|
27
27
|
message_dict.update( {'message_type':mtype, 'display_text':f"{hail} {their_call} {grid_rpt}",
|
|
28
28
|
'priority':(mtype == 'to_me' or mtype == 'CQ')} )
|
|
29
|
-
if self.history:
|
|
30
|
-
current_band = self.gui.get_band_info()['current_band']
|
|
31
|
-
hearing_me, wb_text, geo_text = self.history.get_message_extra_info(their_call, current_band)
|
|
32
|
-
display_text = f"{message_dict['hail']} {message_dict['their_call']} {message_dict['grid_rpt']} {hearing_me} {wb_text} {geo_text}"
|
|
33
|
-
message_dict.update( {'hearing_me':hearing_me, 'wb_text':wb_text, 'geo_text':geo_text, 'display_text':display_text } )
|
|
34
|
-
|
|
35
29
|
if message_dict['priority']:
|
|
36
30
|
if self.gui:
|
|
37
31
|
self.gui.display_message(message_dict)
|
|
32
|
+
|
|
38
33
|
if self.on_decode:
|
|
39
34
|
self.on_decode(message_dict)
|
|
40
35
|
m = message_dict
|
|
@@ -52,7 +47,15 @@ class Broker():
|
|
|
52
47
|
if self.gui:
|
|
53
48
|
band_info = self.gui.get_band_info()
|
|
54
49
|
if not m['priority']:
|
|
55
|
-
self.gui.display_message(m)
|
|
50
|
+
self.gui.display_message(m)
|
|
51
|
+
else:
|
|
52
|
+
if self.history:
|
|
53
|
+
current_band = self.gui.get_band_info()['current_band']
|
|
54
|
+
hearing_me, wb_text, geo_text = self.history.get_message_extra_info(m['their_call'], current_band)
|
|
55
|
+
new_display_text = f"{m['display_text']} {hearing_me} {wb_text} {geo_text}"
|
|
56
|
+
self.gui.update_message( m['display_text'], {'hearing_me':hearing_me, 'wb_text':wb_text,
|
|
57
|
+
'geo_text':geo_text, 'display_text':new_display_text } )
|
|
58
|
+
|
|
56
59
|
if band_info:
|
|
57
60
|
if m['their_call'] != 'not':
|
|
58
61
|
if self.history:
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
|
|
2
|
+
import numpy as np
|
|
3
|
+
import matplotlib.pyplot as plt
|
|
4
|
+
import matplotlib as mpl
|
|
5
|
+
import time
|
|
6
|
+
import threading
|
|
7
|
+
SIZE = 1000
|
|
8
|
+
|
|
9
|
+
def thread1():
|
|
10
|
+
dat = np.random.rand(SIZE, SIZE)
|
|
11
|
+
while True:
|
|
12
|
+
time.sleep(0.1)
|
|
13
|
+
dat = ( dat * dat )%1
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def oncanvasclick(args):
|
|
17
|
+
t = time.time()
|
|
18
|
+
print(t %60)
|
|
19
|
+
|
|
20
|
+
fig, ax = plt.subplots( figsize=(8,8) )
|
|
21
|
+
ax.set_xlim(0,SIZE)
|
|
22
|
+
ax.set_ylim(0,SIZE)
|
|
23
|
+
threading.Thread(target = thread1, daemon = True).start()
|
|
24
|
+
plt.show(block = False)
|
|
25
|
+
|
|
26
|
+
dat = np.random.rand(SIZE, SIZE)
|
|
27
|
+
img = ax.imshow(dat)
|
|
28
|
+
plt.pause(0.1)
|
|
29
|
+
cid = fig.canvas.mpl_connect('button_press_event', oncanvasclick)
|
|
30
|
+
while True:
|
|
31
|
+
time.sleep(0.1)
|
|
32
|
+
img.set_data(np.random.rand(SIZE, SIZE))
|
|
33
|
+
plt.pause(0.2)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
|
pyft8-3.6.4/tests/spare2.py
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
import numpy as np
|
|
3
|
-
import matplotlib.pyplot as plt
|
|
4
|
-
import time
|
|
5
|
-
SIZE = 500
|
|
6
|
-
|
|
7
|
-
fig, ax = plt.subplots( figsize=(8,8) )
|
|
8
|
-
ax.set_xlim(0,SIZE)
|
|
9
|
-
ax.set_ylim(0,SIZE)
|
|
10
|
-
plt.show(block = False)
|
|
11
|
-
|
|
12
|
-
arts = []
|
|
13
|
-
|
|
14
|
-
from matplotlib.patches import Rectangle
|
|
15
|
-
for i in range(10):
|
|
16
|
-
x, y = SIZE*np.random.rand(1)[0], SIZE*np.random.rand(1)[0]
|
|
17
|
-
rect = Rectangle((x, y), width=100, height=6, alpha=0.6, edgecolor='lime', lw=2)
|
|
18
|
-
art = ax.add_patch(rect)
|
|
19
|
-
arts.append(art)
|
|
20
|
-
ax.draw_artist(art)
|
|
21
|
-
fig.canvas.update()
|
|
22
|
-
fig.canvas.flush_events()
|
|
23
|
-
|
|
24
|
-
time.sleep(1)
|
|
25
|
-
for a in arts:
|
|
26
|
-
a.remove()
|
|
27
|
-
plt.pause(0.001)
|
|
28
|
-
|
|
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
|
|
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
|