PyFT8 3.6.5__tar.gz → 3.7.0__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.5 → pyft8-3.7.0}/PKG-INFO +1 -1
- {pyft8-3.6.5 → pyft8-3.7.0}/PyFT8/gui.py +66 -40
- {pyft8-3.6.5 → pyft8-3.7.0}/PyFT8/message_broker.py +10 -8
- {pyft8-3.6.5 → pyft8-3.7.0}/PyFT8/pyft8.py +2 -2
- {pyft8-3.6.5 → pyft8-3.7.0}/PyFT8/receiver.py +1 -1
- {pyft8-3.6.5 → pyft8-3.7.0}/PyFT8.egg-info/PKG-INFO +1 -1
- {pyft8-3.6.5 → pyft8-3.7.0}/pyproject.toml +1 -1
- pyft8-3.7.0/tests/spare2.py +37 -0
- pyft8-3.6.5/tests/spare2.py +0 -28
- {pyft8-3.6.5 → pyft8-3.7.0}/LICENSE +0 -0
- {pyft8-3.6.5 → pyft8-3.7.0}/MANIFEST.in +0 -0
- {pyft8-3.6.5 → pyft8-3.7.0}/PyFT8/__init__.py +0 -0
- {pyft8-3.6.5 → pyft8-3.7.0}/PyFT8/databases.py +0 -0
- {pyft8-3.6.5 → pyft8-3.7.0}/PyFT8/osd.py +0 -0
- {pyft8-3.6.5 → pyft8-3.7.0}/PyFT8/pskreporter.py +0 -0
- {pyft8-3.6.5 → pyft8-3.7.0}/PyFT8/qso_manager.py +0 -0
- {pyft8-3.6.5 → pyft8-3.7.0}/PyFT8/rigctrl.py +0 -0
- {pyft8-3.6.5 → pyft8-3.7.0}/PyFT8/time_utils.py +0 -0
- {pyft8-3.6.5 → pyft8-3.7.0}/PyFT8/transmitter.py +0 -0
- {pyft8-3.6.5 → pyft8-3.7.0}/PyFT8.egg-info/SOURCES.txt +0 -0
- {pyft8-3.6.5 → pyft8-3.7.0}/PyFT8.egg-info/dependency_links.txt +0 -0
- {pyft8-3.6.5 → pyft8-3.7.0}/PyFT8.egg-info/entry_points.txt +0 -0
- {pyft8-3.6.5 → pyft8-3.7.0}/PyFT8.egg-info/requires.txt +0 -0
- {pyft8-3.6.5 → pyft8-3.7.0}/PyFT8.egg-info/top_level.txt +0 -0
- {pyft8-3.6.5 → pyft8-3.7.0}/README.md +0 -0
- {pyft8-3.6.5 → pyft8-3.7.0}/setup.cfg +0 -0
- {pyft8-3.6.5 → pyft8-3.7.0}/tests/misc/CQ AAAA.py +0 -0
- {pyft8-3.6.5 → pyft8-3.7.0}/tests/misc/animation.py +0 -0
- {pyft8-3.6.5 → pyft8-3.7.0}/tests/misc/bits_loopback.py +0 -0
- {pyft8-3.6.5 → pyft8-3.7.0}/tests/misc/bits_loopback_short.py +0 -0
- {pyft8-3.6.5 → pyft8-3.7.0}/tests/misc/osd.py +0 -0
- {pyft8-3.6.5 → pyft8-3.7.0}/tests/misc/symbols_loopback.py +0 -0
- {pyft8-3.6.5 → pyft8-3.7.0}/tests/misc/test_generate_wav.py +0 -0
- {pyft8-3.6.5 → pyft8-3.7.0}/tests/misc/test_loopback_performance.py +0 -0
- {pyft8-3.6.5 → pyft8-3.7.0}/tests/misc/text.py +0 -0
- {pyft8-3.6.5 → pyft8-3.7.0}/tests/misc/view_worked_before.py +0 -0
- {pyft8-3.6.5 → pyft8-3.7.0}/tests/pipeline/Fine tuning compare IOS_PC.py +0 -0
- {pyft8-3.6.5 → pyft8-3.7.0}/tests/pipeline/Targetted tests.py +0 -0
- {pyft8-3.6.5 → pyft8-3.7.0}/tests/pipeline/test_01.wav +0 -0
- {pyft8-3.6.5 → pyft8-3.7.0}/tests/play_wavs.py +0 -0
- {pyft8-3.6.5 → pyft8-3.7.0}/tests/plot_decode_times.py +0 -0
- {pyft8-3.6.5 → pyft8-3.7.0}/tests/spare.py +0 -0
- {pyft8-3.6.5 → pyft8-3.7.0}/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
|
|
@@ -23,24 +25,31 @@ def add_my_axes(fig, pos):
|
|
|
23
25
|
ax.set_facecolor(TEXT_BACKGROUND_COLOR)
|
|
24
26
|
return ax
|
|
25
27
|
|
|
26
|
-
MESSAGE_TYPES = {'generic':{'bg':'blue', 'fg':'white', 'alpha':0.
|
|
27
|
-
'from_me': {'bg':'yellow', 'fg':'black', 'alpha':0.
|
|
28
|
+
MESSAGE_TYPES = {'generic':{'bg':'blue', 'fg':'white', 'alpha':0.4}, 'CQ':{'bg':'green', 'fg':'white', 'alpha':0.7},
|
|
29
|
+
'from_me': {'bg':'yellow', 'fg':'black', 'alpha':0.9}, 'to_me':{'bg':'red', 'fg':'white', 'alpha':0.9}}
|
|
28
30
|
class Msg_box:
|
|
29
|
-
def __init__(self, fig, ax,
|
|
31
|
+
def __init__(self, fig, ax, w, h):
|
|
30
32
|
from matplotlib.patches import Rectangle
|
|
31
33
|
self.fig, self.ax = fig, ax
|
|
32
|
-
|
|
33
|
-
rect = Rectangle((x, y), w, h, edgecolor='lime', lw=2)
|
|
34
|
+
rect = Rectangle((0, 0), w, h, edgecolor='lime', lw=2)
|
|
34
35
|
self.patch = self.ax.add_patch(rect)
|
|
35
36
|
self.patch.set_visible(False)
|
|
36
|
-
text_inst = self.ax.text(
|
|
37
|
-
text_inst.set_visible(False)
|
|
38
|
-
text_inst.
|
|
39
|
-
self.patch.
|
|
40
|
-
|
|
37
|
+
self.text_inst = self.ax.text(0, 0, '', fontweight = 'bold' )
|
|
38
|
+
self.text_inst.set_visible(False)
|
|
39
|
+
self.text_inst.set_fontfamily('monospace')
|
|
40
|
+
self.artists = [self.patch, self.text_inst]
|
|
41
|
+
|
|
42
|
+
def set_props(self, x, y, message):
|
|
43
|
+
self.xy = (x, y)
|
|
41
44
|
self.message = message
|
|
42
45
|
self.cycle = message['their_tx_cycle']
|
|
43
|
-
|
|
46
|
+
message_type_params = MESSAGE_TYPES[message['message_type']]
|
|
47
|
+
self.text_inst.set_text(message['display_text'])
|
|
48
|
+
self.text_inst.set_color(message_type_params['fg'])
|
|
49
|
+
self.patch.set_facecolor(message_type_params['bg'])
|
|
50
|
+
self.patch.set_alpha(message_type_params['alpha'])
|
|
51
|
+
self.text_inst.set_position((x, y+1))
|
|
52
|
+
self.patch.set_xy((x, y))
|
|
44
53
|
for a in self.artists:
|
|
45
54
|
a.set_visible(True)
|
|
46
55
|
|
|
@@ -166,34 +175,48 @@ class Gui:
|
|
|
166
175
|
self.cid = self.fig.canvas.mpl_connect('button_press_event', self._oncanvasclick)
|
|
167
176
|
|
|
168
177
|
def main_loop(self):
|
|
169
|
-
last_ptr = 0
|
|
170
178
|
self.plt.show(block = False)
|
|
179
|
+
target_update_time = 0.15
|
|
171
180
|
while True:
|
|
181
|
+
self.fig.canvas.flush_events() # for clicks
|
|
182
|
+
tstart_wf_redraw = time_utils.time()
|
|
172
183
|
self.image.set_data(self.waterfall_data['data'])
|
|
173
184
|
if self.needs_redraw:
|
|
174
185
|
self.needs_redraw = False
|
|
175
186
|
plt.pause(0.01)
|
|
176
187
|
else:
|
|
177
188
|
self.ax_wf.draw_artist(self.image)
|
|
189
|
+
self.fig.canvas.flush_events() # for clicks
|
|
178
190
|
for mb in self.msg_boxes:
|
|
179
|
-
mb.
|
|
180
|
-
|
|
191
|
+
if mb.patch.get_visible():
|
|
192
|
+
mb.draw()
|
|
193
|
+
self.fig.canvas.flush_events() # for clicks
|
|
194
|
+
self.fig.canvas.blit(self.ax_wf.bbox) #self.fig.canvas.update()
|
|
181
195
|
self.fig.canvas.flush_events()
|
|
196
|
+
tdelay = target_update_time - (time_utils.time() - tstart_wf_redraw)
|
|
197
|
+
if tdelay > 0.01:
|
|
198
|
+
time_utils.sleep(tdelay)
|
|
182
199
|
|
|
183
|
-
def before_search(self, curr_cycle):
|
|
184
|
-
self._hide_msg_boxes(curr_cycle)
|
|
185
|
-
|
|
186
|
-
def after_search(self,curr_cycle):
|
|
187
|
-
self._refresh_hearing()
|
|
188
|
-
self._refresh_band_buttons()
|
|
200
|
+
def before_search(self, curr_cycle): # would be better with a local trigger
|
|
201
|
+
#self._hide_msg_boxes(curr_cycle) # hide boxes overlapping new decodes
|
|
202
|
+
self._hide_msg_boxes(curr_cycle = None) # hide all boxes
|
|
189
203
|
self._refresh_home_panel()
|
|
190
|
-
self.
|
|
204
|
+
self._refresh_band_buttons()
|
|
205
|
+
self._refresh_hearing()
|
|
191
206
|
|
|
192
207
|
def display_message(self, message):
|
|
208
|
+
mb = self._get_message_box()
|
|
193
209
|
x = int(message['t0'] / self.waterfall_data['dt'] + message['their_tx_cycle'] * self.waterfall_data['pixels_per_cycle'])
|
|
194
210
|
y = int(message['fHz'] / self.waterfall_data['df'])
|
|
195
|
-
mb
|
|
211
|
+
mb.set_props(x, y, message)
|
|
196
212
|
self.msg_boxes.append(mb)
|
|
213
|
+
|
|
214
|
+
def update_message(self, display_text, update_dict):
|
|
215
|
+
for mb in self.msg_boxes:
|
|
216
|
+
if mb.patch.get_visible():
|
|
217
|
+
if mb.message['display_text'] == display_text:
|
|
218
|
+
mb.message.update(update_dict)
|
|
219
|
+
mb.set_props(mb.xy[0], mb.xy[1], mb.message)
|
|
197
220
|
|
|
198
221
|
def get_band_info(self):
|
|
199
222
|
return self.band_info
|
|
@@ -215,18 +238,34 @@ class Gui:
|
|
|
215
238
|
color = row_text['color'])
|
|
216
239
|
self.needs_redraw = True
|
|
217
240
|
|
|
241
|
+
def _get_message_box(self):
|
|
242
|
+
mb = None
|
|
243
|
+
for mb1 in self.msg_boxes:
|
|
244
|
+
if not mb1.patch.get_visible():
|
|
245
|
+
mb = mb1
|
|
246
|
+
break
|
|
247
|
+
if not mb:
|
|
248
|
+
mb = Msg_box(self.fig, self.ax_wf, self.waterfall_data['sig_w'], self.waterfall_data['sig_h'])
|
|
249
|
+
return mb
|
|
250
|
+
|
|
218
251
|
def _set_band(self, band):
|
|
219
252
|
if band in self.configured_bands:
|
|
220
253
|
self.band_info = {'current_band':band, 'fMHz':self.configured_bands[band], 'time_set':time_utils.time()}
|
|
221
254
|
self.console_print(f"[PyFT8] Set band: {self.band_info['current_band']} {self.band_info['fMHz']}")
|
|
255
|
+
for mb in self.msg_boxes:
|
|
256
|
+
mb.hide()
|
|
257
|
+
mb.remove()
|
|
258
|
+
self.msg_boxes = []
|
|
259
|
+
self._refresh_home_panel()
|
|
260
|
+
self._refresh_band_buttons()
|
|
261
|
+
self._refresh_hearing()
|
|
262
|
+
self.needs_redraw = True
|
|
222
263
|
|
|
223
264
|
def _oncanvasclick(self, clickargs):
|
|
224
|
-
#print(f"Click at {time_utils.cycle_time():6.2f}")
|
|
225
|
-
|
|
226
265
|
if clickargs.inaxes is self.ax_wf:
|
|
227
266
|
for mb in self.msg_boxes:
|
|
228
267
|
if mb.contains(clickargs.x, clickargs.y):
|
|
229
|
-
self.console_print(f"[GUI] Clicked on message '{mb.message['msg_text']}
|
|
268
|
+
self.console_print(f"[GUI] Clicked on message '{mb.message['msg_text']}")
|
|
230
269
|
self.qso_manager.on_click({'action':"MESSAGE_CLICK", 'message':mb.message})
|
|
231
270
|
return
|
|
232
271
|
for bb in self.button_boxes:
|
|
@@ -235,28 +274,15 @@ class Gui:
|
|
|
235
274
|
self.needs_redraw = True
|
|
236
275
|
self._set_band(bb.id)
|
|
237
276
|
self.qso_manager.on_click({'action':"SET_BAND", 'fMHz':self.configured_bands[bb.id]})
|
|
238
|
-
self.after_search(curr_cycle = None)
|
|
239
277
|
else:
|
|
240
278
|
action = ["CQ", "RPT_LAST", "TX_OFF"][['CQ', 'Repeat last', 'Tx off'].index(bb.id)]
|
|
241
279
|
self.qso_manager.on_click({'action':action})
|
|
242
280
|
|
|
243
|
-
def _hide_msg_boxes(self, curr_cycle):
|
|
244
|
-
to_hide = [mb for mb in self.msg_boxes if mb.cycle == curr_cycle]
|
|
281
|
+
def _hide_msg_boxes(self, curr_cycle = None):
|
|
282
|
+
to_hide = [mb for mb in self.msg_boxes if (mb.cycle == curr_cycle) or (curr_cycle is None)]
|
|
245
283
|
for mb in to_hide:
|
|
246
284
|
mb.hide()
|
|
247
285
|
self.needs_redraw = True
|
|
248
|
-
|
|
249
|
-
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]
|
|
252
|
-
for mb in to_remove:
|
|
253
|
-
mb.remove()
|
|
254
|
-
self.needs_redraw = True
|
|
255
|
-
|
|
256
|
-
def _text_row(self, ax, x, y, text = '', color = 'white', **args):
|
|
257
|
-
art = ax.text(x, y, text, color = color)
|
|
258
|
-
art.set_fontfamily('monospace')
|
|
259
|
-
return art
|
|
260
286
|
|
|
261
287
|
def _refresh_home_panel(self):
|
|
262
288
|
current_band = self.band_info['current_band']
|
|
@@ -26,20 +26,15 @@ 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
|
|
41
36
|
screen_format = f"{cyclestart_string} {their_snr} {m['dt']:4.1f} {m['fHz']:4.0f} ~ {hail} {their_call} {grid_rpt}"
|
|
42
|
-
print(f"{screen_format:50s} decoded@ {m['decode_completed']:5.1f}s, dec = {m['decode_status']}")
|
|
37
|
+
print(f"{screen_format:50s} decoded@ {m['decode_completed'] %15:5.1f}s, dec = {m['decode_status']}")
|
|
43
38
|
self.message_queue_non_time_critical.put(message_dict)
|
|
44
39
|
|
|
45
40
|
def _process_message_ntc(self):
|
|
@@ -52,7 +47,14 @@ 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 } )
|
|
56
58
|
if band_info:
|
|
57
59
|
if m['their_call'] != 'not':
|
|
58
60
|
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.
|
|
14
|
+
VER = '3.7.0'
|
|
15
15
|
PSKR_REFRESH_MINS = 20
|
|
16
16
|
message_broker = None
|
|
17
17
|
|
|
@@ -136,7 +136,7 @@ def cli():
|
|
|
136
136
|
qso_manager = QSO_manager(message_broker, rig_control, console_print)
|
|
137
137
|
message_broker.gui.register_qso_manager(qso_manager)
|
|
138
138
|
message_broker.rx.register_before_search(message_broker.gui.before_search)
|
|
139
|
-
message_broker.rx.register_after_search(message_broker.gui.after_search)
|
|
139
|
+
#message_broker.rx.register_after_search(message_broker.gui.after_search)
|
|
140
140
|
message_broker.history.incorporate_log_data(qso_manager.adif_logging.cache)
|
|
141
141
|
message_broker.history.start_collect_new()
|
|
142
142
|
|
|
@@ -592,7 +592,7 @@ class Receiver():
|
|
|
592
592
|
'msg_text':f"{hail} {their_call} {grid_rpt}",
|
|
593
593
|
'their_snr':f"{c.snr:+03d}", 'their_tx_cycle':c.origin['odd_even'],
|
|
594
594
|
't0':c.origin['t0'], 'dt':c.origin['t0'] - 0.5, 'fHz':c.origin['f0'],
|
|
595
|
-
'decode_status':c.decode_status, 'decode_completed':time_utils.
|
|
595
|
+
'decode_status':c.decode_status, 'decode_completed':time_utils.time()}
|
|
596
596
|
self.process_message(message_dict)
|
|
597
597
|
|
|
598
598
|
if len(to_decode):
|
|
@@ -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.5/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
|