not1mm 25.3.14__py3-none-any.whl → 25.3.17__py3-none-any.whl
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.
- not1mm/data/new_contest.ui +5 -0
- not1mm/lib/plugin_common.py +1 -1
- not1mm/lib/version.py +1 -1
- not1mm/plugins/10_10_fall_cw.py +1 -1
- not1mm/plugins/10_10_spring_cw.py +1 -1
- not1mm/plugins/10_10_summer_phone.py +1 -1
- not1mm/plugins/10_10_winter_phone.py +1 -1
- not1mm/plugins/arrl_10m.py +1 -1
- not1mm/plugins/arrl_160m.py +1 -1
- not1mm/plugins/arrl_dx_cw.py +1 -1
- not1mm/plugins/arrl_dx_ssb.py +1 -1
- not1mm/plugins/arrl_field_day.py +1 -1
- not1mm/plugins/arrl_rtty_ru.py +1 -1
- not1mm/plugins/arrl_ss_cw.py +1 -1
- not1mm/plugins/arrl_ss_phone.py +1 -1
- not1mm/plugins/arrl_vhf_jan.py +1 -1
- not1mm/plugins/arrl_vhf_jun.py +1 -1
- not1mm/plugins/arrl_vhf_sep.py +1 -1
- not1mm/plugins/canada_day.py +1 -1
- not1mm/plugins/cq_160_cw.py +1 -1
- not1mm/plugins/cq_160_ssb.py +1 -1
- not1mm/plugins/cq_wpx_cw.py +1 -1
- not1mm/plugins/cq_wpx_rtty.py +1 -1
- not1mm/plugins/cq_wpx_ssb.py +1 -1
- not1mm/plugins/cq_ww_cw.py +1 -1
- not1mm/plugins/cq_ww_rtty.py +1 -1
- not1mm/plugins/cq_ww_ssb.py +1 -1
- not1mm/plugins/cwt.py +1 -1
- not1mm/plugins/darc_xmas.py +1 -1
- not1mm/plugins/ea_rtty.py +740 -0
- not1mm/plugins/helvetia.py +1 -1
- not1mm/plugins/iaru_fieldday_r1_cw.py +1 -1
- not1mm/plugins/iaru_fieldday_r1_ssb.py +1 -1
- not1mm/plugins/iaru_hf.py +1 -1
- not1mm/plugins/icwc_mst.py +1 -1
- not1mm/plugins/jidx_cw.py +1 -1
- not1mm/plugins/jidx_ph.py +1 -1
- not1mm/plugins/k1usn_sst.py +1 -1
- not1mm/plugins/labre_rs_digi.py +1 -1
- not1mm/plugins/lz-dx.py +2 -2
- not1mm/plugins/naqp_cw.py +1 -1
- not1mm/plugins/naqp_rtty.py +1 -1
- not1mm/plugins/naqp_ssb.py +1 -1
- not1mm/plugins/phone_weekly_test.py +1 -1
- not1mm/plugins/raem.py +1 -1
- not1mm/plugins/ref_cw.py +1 -1
- not1mm/plugins/ref_ssb.py +1 -1
- not1mm/plugins/stew_perry_topband.py +1 -1
- not1mm/plugins/weekly_rtty.py +1 -1
- not1mm/plugins/winter_field_day.py +1 -1
- {not1mm-25.3.14.dist-info → not1mm-25.3.17.dist-info}/METADATA +7 -1
- {not1mm-25.3.14.dist-info → not1mm-25.3.17.dist-info}/RECORD +56 -55
- {not1mm-25.3.14.dist-info → not1mm-25.3.17.dist-info}/LICENSE +0 -0
- {not1mm-25.3.14.dist-info → not1mm-25.3.17.dist-info}/WHEEL +0 -0
- {not1mm-25.3.14.dist-info → not1mm-25.3.17.dist-info}/entry_points.txt +0 -0
- {not1mm-25.3.14.dist-info → not1mm-25.3.17.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,740 @@
|
|
1
|
+
"""
|
2
|
+
EA RTTY Contest
|
3
|
+
|
4
|
+
Status: Active
|
5
|
+
Geographic Focus: Worldwide
|
6
|
+
Participation: Worldwide
|
7
|
+
Mode: RTTY
|
8
|
+
Bands: 80, 40, 20, 15, 10m
|
9
|
+
Classes: Single Op All Band (QRP/Low/High)
|
10
|
+
Single Op All Band Youth
|
11
|
+
Single Op Single Band
|
12
|
+
Multi-Op
|
13
|
+
Max power: High: >100 watts
|
14
|
+
Low: 100 watts
|
15
|
+
QRP: 5 watts
|
16
|
+
Exchange: EA: RSQ + province
|
17
|
+
non-EA: RSQ + Serial No.
|
18
|
+
Work stations: Once per band
|
19
|
+
QSO Points: EA: 2 points per QSO with EA
|
20
|
+
EA: 1 point per QSO with non-EA
|
21
|
+
non-EA: 3 points per QSO with EA
|
22
|
+
non-EA: 1 point per QSO with non-EA
|
23
|
+
Multipliers: Each EADX100 entity once per band
|
24
|
+
Each EA province once per band
|
25
|
+
Each QSO with EA4URE once per band
|
26
|
+
Each USA, VE, JA or VK call area once per band
|
27
|
+
Score Calculation: Total score = total QSO points x total mults
|
28
|
+
E-mail logs to: (none)
|
29
|
+
Upload log at: http://concursos.ure.es/en/logs/
|
30
|
+
Mail logs to: (none)
|
31
|
+
Find rules at: https://concursos.ure.es/en/eartty/bases/
|
32
|
+
Cabrillo name: EARTTY
|
33
|
+
"""
|
34
|
+
|
35
|
+
# pylint: disable=invalid-name, unused-argument, unused-variable, c-extension-no-member
|
36
|
+
|
37
|
+
# EA1: AV, BU, C, LE, LO, LU, O, OU, P, PO, S, SA, SG, SO, VA, ZA
|
38
|
+
# EA2: BI, HU, NA, SS, TE, VI, Z
|
39
|
+
# EA3: B, GI, L, T
|
40
|
+
# EA4: BA, CC, CR, CU, GU, M, TO
|
41
|
+
# EA5: A, AB, CS, MU, V
|
42
|
+
# EA6: IB
|
43
|
+
# EA7: AL, CA, CO, GR, H, J, MA, SE
|
44
|
+
# EA8: GC, TF
|
45
|
+
# EA9: CE, ML
|
46
|
+
|
47
|
+
|
48
|
+
import datetime
|
49
|
+
import logging
|
50
|
+
|
51
|
+
from pathlib import Path
|
52
|
+
from PyQt6 import QtWidgets
|
53
|
+
|
54
|
+
from not1mm.lib.ham_utility import get_logged_band
|
55
|
+
from not1mm.lib.plugin_common import gen_adif, get_points, online_score_xml
|
56
|
+
from not1mm.lib.version import __version__
|
57
|
+
|
58
|
+
logger = logging.getLogger(__name__)
|
59
|
+
|
60
|
+
EXCHANGE_HINT = "Province or #"
|
61
|
+
|
62
|
+
name = "EA RTTY"
|
63
|
+
mode = "RTTY" # CW SSB BOTH RTTY
|
64
|
+
cabrillo_name = "EA-RTTY"
|
65
|
+
|
66
|
+
columns = [
|
67
|
+
"YYYY-MM-DD HH:MM:SS",
|
68
|
+
"Call",
|
69
|
+
"Freq",
|
70
|
+
"Snt",
|
71
|
+
"Rcv",
|
72
|
+
"SentNr",
|
73
|
+
"RcvNr",
|
74
|
+
"PTS",
|
75
|
+
]
|
76
|
+
|
77
|
+
advance_on_space = [True, True, True, True, True]
|
78
|
+
|
79
|
+
# 1 once per contest, 2 work each band, 3 each band/mode, 4 no dupe checking
|
80
|
+
dupe_type = 2
|
81
|
+
|
82
|
+
|
83
|
+
def init_contest(self) -> None:
|
84
|
+
"""setup plugin"""
|
85
|
+
set_tab_next(self)
|
86
|
+
set_tab_prev(self)
|
87
|
+
interface(self)
|
88
|
+
self.next_field = self.other_2
|
89
|
+
|
90
|
+
|
91
|
+
def interface(self) -> None:
|
92
|
+
"""
|
93
|
+
Setup the user interface.
|
94
|
+
Unhides the input fields and sets the lebels.
|
95
|
+
"""
|
96
|
+
self.field1.show()
|
97
|
+
self.field2.show()
|
98
|
+
self.field3.show()
|
99
|
+
self.field4.show()
|
100
|
+
self.snt_label.setText("SNT")
|
101
|
+
self.field1.setAccessibleName("RST Sent")
|
102
|
+
self.other_label.setText("SentNR")
|
103
|
+
self.field3.setAccessibleName("Sent Number")
|
104
|
+
self.exch_label.setText("Prov or SN")
|
105
|
+
self.field4.setAccessibleName("Province or Serial Number")
|
106
|
+
|
107
|
+
|
108
|
+
def reset_label(self) -> None:
|
109
|
+
"""
|
110
|
+
Reset label after field cleared.
|
111
|
+
Not needed for this contest.
|
112
|
+
"""
|
113
|
+
|
114
|
+
|
115
|
+
def set_tab_next(self) -> None:
|
116
|
+
"""
|
117
|
+
Set TAB Advances.
|
118
|
+
Defines which which of the fields are next to get focus when the TAB key is pressed.
|
119
|
+
"""
|
120
|
+
self.tab_next = {
|
121
|
+
self.callsign: self.sent,
|
122
|
+
self.sent: self.receive,
|
123
|
+
self.receive: self.other_1,
|
124
|
+
self.other_1: self.other_2,
|
125
|
+
self.other_2: self.callsign,
|
126
|
+
}
|
127
|
+
|
128
|
+
|
129
|
+
def set_tab_prev(self) -> None:
|
130
|
+
"""
|
131
|
+
Set TAB Advances.
|
132
|
+
Defines which which of the fields are next to get focus when the Shift-TAB key is pressed.
|
133
|
+
"""
|
134
|
+
self.tab_prev = {
|
135
|
+
self.callsign: self.other_2,
|
136
|
+
self.sent: self.callsign,
|
137
|
+
self.receive: self.sent,
|
138
|
+
self.other_1: self.receive,
|
139
|
+
self.other_2: self.other_1,
|
140
|
+
}
|
141
|
+
|
142
|
+
|
143
|
+
def validate(self) -> bool:
|
144
|
+
"""Not Used"""
|
145
|
+
return True
|
146
|
+
|
147
|
+
|
148
|
+
def set_contact_vars(self) -> None:
|
149
|
+
"""Contest Specific"""
|
150
|
+
self.contact["SNT"] = self.sent.text()
|
151
|
+
self.contact["RCV"] = self.receive.text()
|
152
|
+
self.contact["NR"] = self.other_2.text().upper()
|
153
|
+
self.contact["SentNr"] = self.other_1.text()
|
154
|
+
|
155
|
+
|
156
|
+
def predupe(self) -> None:
|
157
|
+
"""called after callsign entered. Not needed here."""
|
158
|
+
|
159
|
+
|
160
|
+
def prefill(self) -> None:
|
161
|
+
"""
|
162
|
+
Fill the SentNR field with either the next serial number or the province.
|
163
|
+
"""
|
164
|
+
result = self.database.get_serial()
|
165
|
+
serial_nr = str(result.get("serial_nr", "1")).zfill(3)
|
166
|
+
if serial_nr == "None":
|
167
|
+
serial_nr = "001"
|
168
|
+
|
169
|
+
exchange = self.contest_settings.get("SentExchange", "").replace("#", serial_nr)
|
170
|
+
if len(self.other_1.text()) == 0:
|
171
|
+
self.other_1.setText(exchange)
|
172
|
+
|
173
|
+
|
174
|
+
def points(self) -> int:
|
175
|
+
"""
|
176
|
+
Calculate the points for this contact.
|
177
|
+
"""
|
178
|
+
# EA: 2 points per QSO with EA
|
179
|
+
# EA: 1 point per QSO with non-EA
|
180
|
+
# non-EA: 3 points per QSO with EA
|
181
|
+
# non-EA: 1 point per QSO with non-EA
|
182
|
+
|
183
|
+
ea_prefixes = ["EA", "EA1", "EA2", "EA3", "EA4", "EA5", "EA6", "EA7", "EA8", "EA9"]
|
184
|
+
|
185
|
+
me = None
|
186
|
+
him = None
|
187
|
+
|
188
|
+
result = self.cty_lookup(self.station.get("Call", ""))
|
189
|
+
if result:
|
190
|
+
for item in result.items():
|
191
|
+
me = item[1].get("primary_pfx", "")
|
192
|
+
|
193
|
+
result = self.cty_lookup(self.contact.get("Call", ""))
|
194
|
+
if result:
|
195
|
+
for item in result.items():
|
196
|
+
him = item[1].get("primary_pfx", "")
|
197
|
+
|
198
|
+
if me is not None and him is not None:
|
199
|
+
if me in ea_prefixes and him in ea_prefixes:
|
200
|
+
return 2
|
201
|
+
elif me in ea_prefixes and him not in ea_prefixes:
|
202
|
+
return 1
|
203
|
+
elif me not in ea_prefixes and him in ea_prefixes:
|
204
|
+
return 3
|
205
|
+
else:
|
206
|
+
return 1
|
207
|
+
|
208
|
+
return 1
|
209
|
+
|
210
|
+
|
211
|
+
def show_mults(self, rtc=None) -> int:
|
212
|
+
"""Return display string for mults"""
|
213
|
+
|
214
|
+
ea_provinces = 0
|
215
|
+
dx = 0
|
216
|
+
ea4ure = 0
|
217
|
+
eadx100 = 0
|
218
|
+
|
219
|
+
# Each EADX100 entity once per band
|
220
|
+
sql = (
|
221
|
+
"select count(DISTINCT(CountryPrefix || ':' || Band)) as mult_count "
|
222
|
+
f"from dxlog where ContestNR = {self.database.current_contest};"
|
223
|
+
)
|
224
|
+
result = self.database.exec_sql(sql)
|
225
|
+
if result:
|
226
|
+
eadx100 = result.get("mult_count", 0)
|
227
|
+
|
228
|
+
# Each EA province once per band
|
229
|
+
sql = (
|
230
|
+
"select count(DISTINCT(NR || ':' || Band)) as mult_count "
|
231
|
+
f"from dxlog where ContestNR = {self.database.current_contest} and typeof(NR) = 'text';"
|
232
|
+
)
|
233
|
+
result = self.database.exec_sql(sql)
|
234
|
+
if result:
|
235
|
+
ea_provinces = result.get("mult_count", 0)
|
236
|
+
|
237
|
+
# Each USA, VE, JA or VK call area once per band
|
238
|
+
sql = (
|
239
|
+
"select count(DISTINCT(CountryPrefix || ':' || substr(WPXPrefix, -1) || ':' || Band)) as mult_count "
|
240
|
+
f"from dxlog where CountryPrefix in ('K', 'VE', 'VK', 'JA') and ContestNR = {self.database.current_contest};"
|
241
|
+
)
|
242
|
+
result = self.database.exec_sql(sql)
|
243
|
+
if result:
|
244
|
+
dx = result.get("mult_count", 0)
|
245
|
+
|
246
|
+
# Each QSO with EA4URE once per band
|
247
|
+
sql = (
|
248
|
+
"select count(DISTINCT(Band)) as mult_count "
|
249
|
+
f"from dxlog where Call = 'EA4URE' and ContestNR = {self.database.current_contest};"
|
250
|
+
)
|
251
|
+
result = self.database.exec_sql(sql)
|
252
|
+
if result:
|
253
|
+
ea4ure = result.get("mult_count", 0)
|
254
|
+
|
255
|
+
if rtc is not None:
|
256
|
+
return 0, 0
|
257
|
+
|
258
|
+
return ea_provinces + dx + ea4ure + eadx100
|
259
|
+
|
260
|
+
|
261
|
+
def show_qso(self) -> int:
|
262
|
+
"""Return qso count"""
|
263
|
+
result = self.database.fetch_qso_count()
|
264
|
+
if result:
|
265
|
+
return int(result.get("qsos", 0))
|
266
|
+
return 0
|
267
|
+
|
268
|
+
|
269
|
+
def calc_score(self) -> int:
|
270
|
+
"""Return calculated score"""
|
271
|
+
_points = get_points(self)
|
272
|
+
_mults = show_mults(self)
|
273
|
+
|
274
|
+
return _points * _mults
|
275
|
+
|
276
|
+
|
277
|
+
def adif(self) -> None:
|
278
|
+
"""Call the generate ADIF function"""
|
279
|
+
gen_adif(self, cabrillo_name)
|
280
|
+
|
281
|
+
|
282
|
+
def output_cabrillo_line(line_to_output, ending, file_descriptor, file_encoding):
|
283
|
+
""""""
|
284
|
+
print(
|
285
|
+
line_to_output.encode(file_encoding, errors="ignore").decode(),
|
286
|
+
end=ending,
|
287
|
+
file=file_descriptor,
|
288
|
+
)
|
289
|
+
|
290
|
+
|
291
|
+
def cabrillo(self, file_encoding):
|
292
|
+
"""Generates Cabrillo file. Maybe."""
|
293
|
+
# https://www.cqwpx.com/cabrillo.htm
|
294
|
+
logger.debug("******Cabrillo*****")
|
295
|
+
logger.debug("Station: %s", f"{self.station}")
|
296
|
+
logger.debug("Contest: %s", f"{self.contest_settings}")
|
297
|
+
now = datetime.datetime.now()
|
298
|
+
date_time = now.strftime("%Y-%m-%d_%H-%M-%S")
|
299
|
+
filename = (
|
300
|
+
str(Path.home())
|
301
|
+
+ "/"
|
302
|
+
+ f"{self.station.get('Call', '').upper()}_{cabrillo_name}_{date_time}.log"
|
303
|
+
)
|
304
|
+
logger.debug("%s", filename)
|
305
|
+
log = self.database.fetch_all_contacts_asc()
|
306
|
+
try:
|
307
|
+
with open(filename, "w", encoding=file_encoding, newline="") as file_descriptor:
|
308
|
+
output_cabrillo_line(
|
309
|
+
"START-OF-LOG: 3.0",
|
310
|
+
"\r\n",
|
311
|
+
file_descriptor,
|
312
|
+
file_encoding,
|
313
|
+
)
|
314
|
+
output_cabrillo_line(
|
315
|
+
f"CREATED-BY: Not1MM v{__version__}",
|
316
|
+
"\r\n",
|
317
|
+
file_descriptor,
|
318
|
+
file_encoding,
|
319
|
+
)
|
320
|
+
output_cabrillo_line(
|
321
|
+
f"CONTEST: {cabrillo_name}",
|
322
|
+
"\r\n",
|
323
|
+
file_descriptor,
|
324
|
+
file_encoding,
|
325
|
+
)
|
326
|
+
if self.station.get("Club", ""):
|
327
|
+
output_cabrillo_line(
|
328
|
+
f"CLUB: {self.station.get('Club', '')}",
|
329
|
+
"\r\n",
|
330
|
+
file_descriptor,
|
331
|
+
file_encoding,
|
332
|
+
)
|
333
|
+
output_cabrillo_line(
|
334
|
+
f"CALLSIGN: {self.station.get('Call','')}",
|
335
|
+
"\r\n",
|
336
|
+
file_descriptor,
|
337
|
+
file_encoding,
|
338
|
+
)
|
339
|
+
output_cabrillo_line(
|
340
|
+
f"LOCATION: {self.station.get('ARRLSection', '')}",
|
341
|
+
"\r\n",
|
342
|
+
file_descriptor,
|
343
|
+
file_encoding,
|
344
|
+
)
|
345
|
+
output_cabrillo_line(
|
346
|
+
f"CATEGORY-OPERATOR: {self.contest_settings.get('OperatorCategory','')}",
|
347
|
+
"\r\n",
|
348
|
+
file_descriptor,
|
349
|
+
file_encoding,
|
350
|
+
)
|
351
|
+
output_cabrillo_line(
|
352
|
+
f"CATEGORY-ASSISTED: {self.contest_settings.get('AssistedCategory','')}",
|
353
|
+
"\r\n",
|
354
|
+
file_descriptor,
|
355
|
+
file_encoding,
|
356
|
+
)
|
357
|
+
output_cabrillo_line(
|
358
|
+
f"CATEGORY-BAND: {self.contest_settings.get('BandCategory','')}",
|
359
|
+
"\r\n",
|
360
|
+
file_descriptor,
|
361
|
+
file_encoding,
|
362
|
+
)
|
363
|
+
mode = self.contest_settings.get("ModeCategory", "")
|
364
|
+
if mode in ["SSB+CW", "SSB+CW+DIGITAL"]:
|
365
|
+
mode = "MIXED"
|
366
|
+
output_cabrillo_line(
|
367
|
+
f"CATEGORY-MODE: {mode}",
|
368
|
+
"\r\n",
|
369
|
+
file_descriptor,
|
370
|
+
file_encoding,
|
371
|
+
)
|
372
|
+
output_cabrillo_line(
|
373
|
+
f"CATEGORY-TRANSMITTER: {self.contest_settings.get('TransmitterCategory','')}",
|
374
|
+
"\r\n",
|
375
|
+
file_descriptor,
|
376
|
+
file_encoding,
|
377
|
+
)
|
378
|
+
if self.contest_settings.get("OverlayCategory", "") != "N/A":
|
379
|
+
output_cabrillo_line(
|
380
|
+
f"CATEGORY-OVERLAY: {self.contest_settings.get('OverlayCategory','')}",
|
381
|
+
"\r\n",
|
382
|
+
file_descriptor,
|
383
|
+
file_encoding,
|
384
|
+
)
|
385
|
+
output_cabrillo_line(
|
386
|
+
f"GRID-LOCATOR: {self.station.get('GridSquare','')}",
|
387
|
+
"\r\n",
|
388
|
+
file_descriptor,
|
389
|
+
file_encoding,
|
390
|
+
)
|
391
|
+
output_cabrillo_line(
|
392
|
+
f"CATEGORY-POWER: {self.contest_settings.get('PowerCategory','')}",
|
393
|
+
"\r\n",
|
394
|
+
file_descriptor,
|
395
|
+
file_encoding,
|
396
|
+
)
|
397
|
+
|
398
|
+
output_cabrillo_line(
|
399
|
+
f"CLAIMED-SCORE: {calc_score(self)}",
|
400
|
+
"\r\n",
|
401
|
+
file_descriptor,
|
402
|
+
file_encoding,
|
403
|
+
)
|
404
|
+
ops = f"@{self.station.get('Call','')}"
|
405
|
+
list_of_ops = self.database.get_ops()
|
406
|
+
for op in list_of_ops:
|
407
|
+
ops += f", {op.get('Operator', '')}"
|
408
|
+
output_cabrillo_line(
|
409
|
+
f"OPERATORS: {ops}",
|
410
|
+
"\r\n",
|
411
|
+
file_descriptor,
|
412
|
+
file_encoding,
|
413
|
+
)
|
414
|
+
output_cabrillo_line(
|
415
|
+
f"NAME: {self.station.get('Name', '')}",
|
416
|
+
"\r\n",
|
417
|
+
file_descriptor,
|
418
|
+
file_encoding,
|
419
|
+
)
|
420
|
+
output_cabrillo_line(
|
421
|
+
f"ADDRESS: {self.station.get('Street1', '')}",
|
422
|
+
"\r\n",
|
423
|
+
file_descriptor,
|
424
|
+
file_encoding,
|
425
|
+
)
|
426
|
+
output_cabrillo_line(
|
427
|
+
f"ADDRESS-CITY: {self.station.get('City', '')}",
|
428
|
+
"\r\n",
|
429
|
+
file_descriptor,
|
430
|
+
file_encoding,
|
431
|
+
)
|
432
|
+
output_cabrillo_line(
|
433
|
+
f"ADDRESS-STATE-PROVINCE: {self.station.get('State', '')}",
|
434
|
+
"\r\n",
|
435
|
+
file_descriptor,
|
436
|
+
file_encoding,
|
437
|
+
)
|
438
|
+
output_cabrillo_line(
|
439
|
+
f"ADDRESS-POSTALCODE: {self.station.get('Zip', '')}",
|
440
|
+
"\r\n",
|
441
|
+
file_descriptor,
|
442
|
+
file_encoding,
|
443
|
+
)
|
444
|
+
output_cabrillo_line(
|
445
|
+
f"ADDRESS-COUNTRY: {self.station.get('Country', '')}",
|
446
|
+
"\r\n",
|
447
|
+
file_descriptor,
|
448
|
+
file_encoding,
|
449
|
+
)
|
450
|
+
output_cabrillo_line(
|
451
|
+
f"EMAIL: {self.station.get('Email', '')}",
|
452
|
+
"\r\n",
|
453
|
+
file_descriptor,
|
454
|
+
file_encoding,
|
455
|
+
)
|
456
|
+
for contact in log:
|
457
|
+
the_date_and_time = contact.get("TS", "")
|
458
|
+
themode = contact.get("Mode", "")
|
459
|
+
if themode == "LSB" or themode == "USB":
|
460
|
+
themode = "PH"
|
461
|
+
if themode == "RTTY":
|
462
|
+
themode = "RY"
|
463
|
+
frequency = str(int(contact.get("Freq", "0"))).rjust(5)
|
464
|
+
|
465
|
+
loggeddate = the_date_and_time[:10]
|
466
|
+
loggedtime = the_date_and_time[11:13] + the_date_and_time[14:16]
|
467
|
+
output_cabrillo_line(
|
468
|
+
f"QSO: {frequency} {themode} {loggeddate} {loggedtime} "
|
469
|
+
f"{contact.get('StationPrefix', '').ljust(13)} "
|
470
|
+
f"{str(contact.get('SNT', '')).ljust(3)} "
|
471
|
+
f"{str(contact.get('SentNr', '')).ljust(6)} "
|
472
|
+
f"{contact.get('Call', '').ljust(13)} "
|
473
|
+
f"{str(contact.get('RCV', '')).ljust(3)} "
|
474
|
+
f"{str(contact.get('NR', '')).ljust(6)}",
|
475
|
+
"\r\n",
|
476
|
+
file_descriptor,
|
477
|
+
file_encoding,
|
478
|
+
)
|
479
|
+
output_cabrillo_line("END-OF-LOG:", "\r\n", file_descriptor, file_encoding)
|
480
|
+
self.show_message_box(f"Cabrillo saved to: {filename}")
|
481
|
+
except IOError as exception:
|
482
|
+
logger.critical("cabrillo: IO error: %s, writing to %s", exception, filename)
|
483
|
+
self.show_message_box(f"Error saving Cabrillo: {exception} {filename}")
|
484
|
+
return
|
485
|
+
|
486
|
+
|
487
|
+
def recalculate_mults(self) -> None:
|
488
|
+
"""Recalculates multipliers after change in logged qso."""
|
489
|
+
|
490
|
+
|
491
|
+
def process_esm(self, new_focused_widget=None, with_enter=False):
|
492
|
+
"""ESM State Machine"""
|
493
|
+
|
494
|
+
# self.pref["run_state"]
|
495
|
+
|
496
|
+
# -----===== Assigned F-Keys =====-----
|
497
|
+
# self.esm_dict["CQ"]
|
498
|
+
# self.esm_dict["EXCH"]
|
499
|
+
# self.esm_dict["QRZ"]
|
500
|
+
# self.esm_dict["AGN"]
|
501
|
+
# self.esm_dict["HISCALL"]
|
502
|
+
# self.esm_dict["MYCALL"]
|
503
|
+
# self.esm_dict["QSOB4"]
|
504
|
+
|
505
|
+
# ----==== text fields ====----
|
506
|
+
# self.callsign
|
507
|
+
# self.sent
|
508
|
+
# self.receive
|
509
|
+
# self.other_1
|
510
|
+
# self.other_2
|
511
|
+
|
512
|
+
if new_focused_widget is not None:
|
513
|
+
self.current_widget = self.inputs_dict.get(new_focused_widget)
|
514
|
+
|
515
|
+
# print(f"checking esm {self.current_widget=} {with_enter=} {self.pref.get("run_state")=}")
|
516
|
+
|
517
|
+
for a_button in [
|
518
|
+
self.esm_dict["CQ"],
|
519
|
+
self.esm_dict["EXCH"],
|
520
|
+
self.esm_dict["QRZ"],
|
521
|
+
self.esm_dict["AGN"],
|
522
|
+
self.esm_dict["HISCALL"],
|
523
|
+
self.esm_dict["MYCALL"],
|
524
|
+
self.esm_dict["QSOB4"],
|
525
|
+
]:
|
526
|
+
if a_button is not None:
|
527
|
+
self.restore_button_color(a_button)
|
528
|
+
|
529
|
+
buttons_to_send = []
|
530
|
+
|
531
|
+
if self.pref.get("run_state"):
|
532
|
+
if self.current_widget == "callsign":
|
533
|
+
if len(self.callsign.text()) < 3:
|
534
|
+
self.make_button_green(self.esm_dict["CQ"])
|
535
|
+
buttons_to_send.append(self.esm_dict["CQ"])
|
536
|
+
elif len(self.callsign.text()) > 2:
|
537
|
+
self.make_button_green(self.esm_dict["HISCALL"])
|
538
|
+
self.make_button_green(self.esm_dict["EXCH"])
|
539
|
+
buttons_to_send.append(self.esm_dict["HISCALL"])
|
540
|
+
buttons_to_send.append(self.esm_dict["EXCH"])
|
541
|
+
|
542
|
+
elif self.current_widget in ["other_2"]:
|
543
|
+
if self.other_2.text() == "":
|
544
|
+
self.make_button_green(self.esm_dict["AGN"])
|
545
|
+
buttons_to_send.append(self.esm_dict["AGN"])
|
546
|
+
else:
|
547
|
+
self.make_button_green(self.esm_dict["QRZ"])
|
548
|
+
buttons_to_send.append(self.esm_dict["QRZ"])
|
549
|
+
buttons_to_send.append("LOGIT")
|
550
|
+
|
551
|
+
if with_enter is True and bool(len(buttons_to_send)):
|
552
|
+
for button in buttons_to_send:
|
553
|
+
if button:
|
554
|
+
if button == "LOGIT":
|
555
|
+
self.save_contact()
|
556
|
+
continue
|
557
|
+
if button == self.esm_dict["HISCALL"]:
|
558
|
+
self.process_function_key(button, rttysendrx=False)
|
559
|
+
continue
|
560
|
+
self.process_function_key(button)
|
561
|
+
else:
|
562
|
+
if self.current_widget == "callsign":
|
563
|
+
if len(self.callsign.text()) > 2:
|
564
|
+
self.make_button_green(self.esm_dict["MYCALL"])
|
565
|
+
buttons_to_send.append(self.esm_dict["MYCALL"])
|
566
|
+
|
567
|
+
elif self.current_widget in ["other_2"]:
|
568
|
+
if self.other_2.text() == "":
|
569
|
+
self.make_button_green(self.esm_dict["AGN"])
|
570
|
+
buttons_to_send.append(self.esm_dict["AGN"])
|
571
|
+
else:
|
572
|
+
self.make_button_green(self.esm_dict["EXCH"])
|
573
|
+
buttons_to_send.append(self.esm_dict["EXCH"])
|
574
|
+
buttons_to_send.append("LOGIT")
|
575
|
+
|
576
|
+
if with_enter is True and bool(len(buttons_to_send)):
|
577
|
+
for button in buttons_to_send:
|
578
|
+
if button:
|
579
|
+
if button == "LOGIT":
|
580
|
+
self.save_contact()
|
581
|
+
continue
|
582
|
+
self.process_function_key(button)
|
583
|
+
|
584
|
+
|
585
|
+
def populate_history_info_line(self):
|
586
|
+
result = self.database.fetch_call_history(self.callsign.text())
|
587
|
+
if result:
|
588
|
+
self.history_info.setText(
|
589
|
+
f"{result.get('Call', '')}, {result.get('Name', '')}, {result.get('State', '')}, {result.get('UserText','...')}"
|
590
|
+
)
|
591
|
+
else:
|
592
|
+
self.history_info.setText("")
|
593
|
+
|
594
|
+
|
595
|
+
def check_call_history(self):
|
596
|
+
""""""
|
597
|
+
result = self.database.fetch_call_history(self.callsign.text())
|
598
|
+
if result:
|
599
|
+
self.history_info.setText(f"{result.get('UserText','')}")
|
600
|
+
if self.other_2.text() == "":
|
601
|
+
self.other_2.setText(f"{result.get('State', '')}")
|
602
|
+
|
603
|
+
|
604
|
+
def get_mults(self):
|
605
|
+
""""""
|
606
|
+
mults = {}
|
607
|
+
mults["country"], mults["state"] = show_mults(self, rtc=True)
|
608
|
+
return mults
|
609
|
+
|
610
|
+
|
611
|
+
def just_points(self):
|
612
|
+
""""""
|
613
|
+
return get_points(self)
|
614
|
+
|
615
|
+
|
616
|
+
def set_self(the_outie):
|
617
|
+
"""..."""
|
618
|
+
globals()["ALTEREGO"] = the_outie
|
619
|
+
|
620
|
+
|
621
|
+
def ft8_handler(the_packet: dict):
|
622
|
+
print(f"{the_packet=}")
|
623
|
+
"""Process FT8 QSO packets
|
624
|
+
FT8
|
625
|
+
{
|
626
|
+
'CALL': 'KE0OG',
|
627
|
+
'GRIDSQUARE': 'DM10AT',
|
628
|
+
'MODE': 'FT8',
|
629
|
+
'RST_SENT': '',
|
630
|
+
'RST_RCVD': '',
|
631
|
+
'QSO_DATE': '20210329',
|
632
|
+
'TIME_ON': '183213',
|
633
|
+
'QSO_DATE_OFF': '20210329',
|
634
|
+
'TIME_OFF': '183213',
|
635
|
+
'BAND': '20M',
|
636
|
+
'FREQ': '14.074754',
|
637
|
+
'STATION_CALLSIGN': 'K6GTE',
|
638
|
+
'MY_GRIDSQUARE': 'DM13AT',
|
639
|
+
'CONTEST_ID': 'ARRL-FIELD-DAY',
|
640
|
+
'SRX_STRING': '1D UT',
|
641
|
+
'CLASS': '1D',
|
642
|
+
'ARRL_SECT': 'UT'
|
643
|
+
}
|
644
|
+
FlDigi
|
645
|
+
{
|
646
|
+
'CALL': 'K5TUS',
|
647
|
+
'MODE': 'RTTY',
|
648
|
+
'FREQ': '14.068415',
|
649
|
+
'BAND': '20M',
|
650
|
+
'QSO_DATE': '20250103',
|
651
|
+
'TIME_ON': '2359',
|
652
|
+
'QSO_DATE_OFF': '20250103',
|
653
|
+
'TIME_OFF': '2359',
|
654
|
+
'NAME': '',
|
655
|
+
'QTH': '',
|
656
|
+
'STATE': 'ORG',
|
657
|
+
'VE_PROV': '',
|
658
|
+
'COUNTRY': 'USA',
|
659
|
+
'RST_SENT': '599',
|
660
|
+
'RST_RCVD': '599',
|
661
|
+
'TX_PWR': '0',
|
662
|
+
'CNTY': '',
|
663
|
+
'DXCC': '',
|
664
|
+
'CQZ': '5',
|
665
|
+
'IOTA': '',
|
666
|
+
'CONT': '',
|
667
|
+
'ITUZ': '',
|
668
|
+
'GRIDSQUARE': '',
|
669
|
+
'QSLRDATE': '',
|
670
|
+
'QSLSDATE': '',
|
671
|
+
'EQSLRDATE': '',
|
672
|
+
'EQSLSDATE': '',
|
673
|
+
'LOTWRDATE': '',
|
674
|
+
'LOTWSDATE': '',
|
675
|
+
'QSL_VIA': '',
|
676
|
+
'NOTES': '',
|
677
|
+
'SRX': '',
|
678
|
+
'STX': '000',
|
679
|
+
'SRX_STRING': '',
|
680
|
+
'STX_STRING': 'CA',
|
681
|
+
|
682
|
+
|
683
|
+
'SRX': '666',
|
684
|
+
'STX': '000',
|
685
|
+
'SRX_STRING': '',
|
686
|
+
'STX_STRING': 'CA',
|
687
|
+
|
688
|
+
'SRX': '004', 'STX': '000', 'SRX_STRING': '', 'STX_STRING': '#',
|
689
|
+
|
690
|
+
'CLASS': '',
|
691
|
+
'ARRL_SECT': '',
|
692
|
+
'OPERATOR': 'K6GTE',
|
693
|
+
'STATION_CALLSIGN': 'K6GTE',
|
694
|
+
'MY_GRIDSQUARE': 'DM13AT',
|
695
|
+
'MY_CITY': 'ANAHEIM, CA',
|
696
|
+
'CHECK': '',
|
697
|
+
'AGE': '',
|
698
|
+
'TEN_TEN': '',
|
699
|
+
'CWSS_PREC': '',
|
700
|
+
'CWSS_SECTION': '',
|
701
|
+
'CWSS_SERNO': '',
|
702
|
+
'CWSS_CHK': ''
|
703
|
+
}
|
704
|
+
|
705
|
+
"""
|
706
|
+
|
707
|
+
logger.debug(f"{the_packet=}")
|
708
|
+
if ALTEREGO is not None:
|
709
|
+
ALTEREGO.callsign.setText(the_packet.get("CALL"))
|
710
|
+
ALTEREGO.contact["Call"] = the_packet.get("CALL", "")
|
711
|
+
ALTEREGO.contact["SNT"] = the_packet.get("RST_SENT", "599")
|
712
|
+
ALTEREGO.contact["RCV"] = the_packet.get("RST_RCVD", "599")
|
713
|
+
|
714
|
+
sent_string = the_packet.get("STX_STRING", "")
|
715
|
+
if sent_string != "":
|
716
|
+
ALTEREGO.contact["SentNr"] = sent_string
|
717
|
+
ALTEREGO.other_1.setText(str(sent_string))
|
718
|
+
else:
|
719
|
+
ALTEREGO.contact["SentNr"] = the_packet.get("STX", "000")
|
720
|
+
ALTEREGO.other_1.setText(str(the_packet.get("STX", "000")))
|
721
|
+
|
722
|
+
rx_string = the_packet.get("SRX_STRING", "")
|
723
|
+
if rx_string != "":
|
724
|
+
ALTEREGO.contact["NR"] = rx_string
|
725
|
+
ALTEREGO.other_2.setText(str(rx_string))
|
726
|
+
else:
|
727
|
+
ALTEREGO.contact["NR"] = the_packet.get("SRX", "000")
|
728
|
+
ALTEREGO.other_2.setText(str(the_packet.get("SRX", "000")))
|
729
|
+
|
730
|
+
ALTEREGO.contact["Mode"] = the_packet.get("MODE", "ERR")
|
731
|
+
ALTEREGO.contact["Freq"] = round(float(the_packet.get("FREQ", "0.0")) * 1000, 2)
|
732
|
+
ALTEREGO.contact["QSXFreq"] = round(
|
733
|
+
float(the_packet.get("FREQ", "0.0")) * 1000, 2
|
734
|
+
)
|
735
|
+
ALTEREGO.contact["Band"] = get_logged_band(
|
736
|
+
str(int(float(the_packet.get("FREQ", "0.0")) * 1000000))
|
737
|
+
)
|
738
|
+
logger.debug(f"{ALTEREGO.contact=}")
|
739
|
+
|
740
|
+
ALTEREGO.save_contact()
|