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.
Files changed (43) hide show
  1. {pyft8-3.6.4 → pyft8-3.6.6}/PKG-INFO +1 -1
  2. {pyft8-3.6.4 → pyft8-3.6.6}/PyFT8/gui.py +56 -19
  3. {pyft8-3.6.4 → pyft8-3.6.6}/PyFT8/message_broker.py +10 -7
  4. {pyft8-3.6.4 → pyft8-3.6.6}/PyFT8/pyft8.py +1 -1
  5. {pyft8-3.6.4 → pyft8-3.6.6}/PyFT8.egg-info/PKG-INFO +1 -1
  6. {pyft8-3.6.4 → pyft8-3.6.6}/pyproject.toml +1 -1
  7. pyft8-3.6.6/tests/spare2.py +37 -0
  8. pyft8-3.6.4/tests/spare2.py +0 -28
  9. {pyft8-3.6.4 → pyft8-3.6.6}/LICENSE +0 -0
  10. {pyft8-3.6.4 → pyft8-3.6.6}/MANIFEST.in +0 -0
  11. {pyft8-3.6.4 → pyft8-3.6.6}/PyFT8/__init__.py +0 -0
  12. {pyft8-3.6.4 → pyft8-3.6.6}/PyFT8/databases.py +0 -0
  13. {pyft8-3.6.4 → pyft8-3.6.6}/PyFT8/osd.py +0 -0
  14. {pyft8-3.6.4 → pyft8-3.6.6}/PyFT8/pskreporter.py +0 -0
  15. {pyft8-3.6.4 → pyft8-3.6.6}/PyFT8/qso_manager.py +0 -0
  16. {pyft8-3.6.4 → pyft8-3.6.6}/PyFT8/receiver.py +0 -0
  17. {pyft8-3.6.4 → pyft8-3.6.6}/PyFT8/rigctrl.py +0 -0
  18. {pyft8-3.6.4 → pyft8-3.6.6}/PyFT8/time_utils.py +0 -0
  19. {pyft8-3.6.4 → pyft8-3.6.6}/PyFT8/transmitter.py +0 -0
  20. {pyft8-3.6.4 → pyft8-3.6.6}/PyFT8.egg-info/SOURCES.txt +0 -0
  21. {pyft8-3.6.4 → pyft8-3.6.6}/PyFT8.egg-info/dependency_links.txt +0 -0
  22. {pyft8-3.6.4 → pyft8-3.6.6}/PyFT8.egg-info/entry_points.txt +0 -0
  23. {pyft8-3.6.4 → pyft8-3.6.6}/PyFT8.egg-info/requires.txt +0 -0
  24. {pyft8-3.6.4 → pyft8-3.6.6}/PyFT8.egg-info/top_level.txt +0 -0
  25. {pyft8-3.6.4 → pyft8-3.6.6}/README.md +0 -0
  26. {pyft8-3.6.4 → pyft8-3.6.6}/setup.cfg +0 -0
  27. {pyft8-3.6.4 → pyft8-3.6.6}/tests/misc/CQ AAAA.py +0 -0
  28. {pyft8-3.6.4 → pyft8-3.6.6}/tests/misc/animation.py +0 -0
  29. {pyft8-3.6.4 → pyft8-3.6.6}/tests/misc/bits_loopback.py +0 -0
  30. {pyft8-3.6.4 → pyft8-3.6.6}/tests/misc/bits_loopback_short.py +0 -0
  31. {pyft8-3.6.4 → pyft8-3.6.6}/tests/misc/osd.py +0 -0
  32. {pyft8-3.6.4 → pyft8-3.6.6}/tests/misc/symbols_loopback.py +0 -0
  33. {pyft8-3.6.4 → pyft8-3.6.6}/tests/misc/test_generate_wav.py +0 -0
  34. {pyft8-3.6.4 → pyft8-3.6.6}/tests/misc/test_loopback_performance.py +0 -0
  35. {pyft8-3.6.4 → pyft8-3.6.6}/tests/misc/text.py +0 -0
  36. {pyft8-3.6.4 → pyft8-3.6.6}/tests/misc/view_worked_before.py +0 -0
  37. {pyft8-3.6.4 → pyft8-3.6.6}/tests/pipeline/Fine tuning compare IOS_PC.py +0 -0
  38. {pyft8-3.6.4 → pyft8-3.6.6}/tests/pipeline/Targetted tests.py +0 -0
  39. {pyft8-3.6.4 → pyft8-3.6.6}/tests/pipeline/test_01.wav +0 -0
  40. {pyft8-3.6.4 → pyft8-3.6.6}/tests/play_wavs.py +0 -0
  41. {pyft8-3.6.4 → pyft8-3.6.6}/tests/plot_decode_times.py +0 -0
  42. {pyft8-3.6.4 → pyft8-3.6.6}/tests/spare.py +0 -0
  43. {pyft8-3.6.4 → pyft8-3.6.6}/tests/test_batch_and_live.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: PyFT8
3
- Version: 3.6.4
3
+ Version: 3.6.6
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,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((x, y), w, h, edgecolor='lime', lw=2)
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(x, y+1, message['display_text'], fontsize='small', fontweight = 'bold' )
37
- text_inst.set_visible(False)
38
- text_inst.set_color(message_type_params['fg'])
39
- self.patch.set_facecolor(message_type_params['bg'])
40
- self.patch.set_alpha(message_type_params['alpha'])
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
- self.artists = [self.patch, text_inst]
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.fig.canvas.update()
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 = Msg_box(self.fig, self.ax_wf, x, y, self.waterfall_data['sig_w'], self.waterfall_data['sig_h'], message)
196
- self.msg_boxes.append(mb)
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:
@@ -11,7 +11,7 @@ from PyFT8.databases import ADIF
11
11
  from PyFT8.qso_manager import QSO_manager
12
12
  from PyFT8.message_broker import Broker
13
13
 
14
- VER = '3.6.4'
14
+ VER = '3.6.6'
15
15
  PSKR_REFRESH_MINS = 20
16
16
  message_broker = None
17
17
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: PyFT8
3
- Version: 3.6.4
3
+ Version: 3.6.6
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 = "3.6.4"
3
+ version = "3.6.6"
4
4
  license = "GPL-3.0-or-later"
5
5
 
6
6
  authors = [
@@ -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
+
@@ -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