not1mm 24.9.8__py3-none-any.whl → 24.9.10__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/__main__.py +9 -1
- not1mm/data/new_contest.ui +28 -1
- not1mm/lib/database.py +28 -0
- not1mm/lib/version.py +1 -1
- not1mm/plugins/arrl_vhf_jan.py +105 -2
- not1mm/plugins/arrl_vhf_jun.py +105 -2
- not1mm/plugins/arrl_vhf_sep.py +105 -2
- not1mm/plugins/iaru_fieldday_r1_cw.py +421 -0
- not1mm/plugins/iaru_fieldday_r1_ssb.py +421 -0
- {not1mm-24.9.8.dist-info → not1mm-24.9.10.dist-info}/METADATA +7 -3
- {not1mm-24.9.8.dist-info → not1mm-24.9.10.dist-info}/RECORD +15 -13
- {not1mm-24.9.8.dist-info → not1mm-24.9.10.dist-info}/LICENSE +0 -0
- {not1mm-24.9.8.dist-info → not1mm-24.9.10.dist-info}/WHEEL +0 -0
- {not1mm-24.9.8.dist-info → not1mm-24.9.10.dist-info}/entry_points.txt +0 -0
- {not1mm-24.9.8.dist-info → not1mm-24.9.10.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,421 @@
|
|
1
|
+
"""IARU Region 1 Field Day"""
|
2
|
+
|
3
|
+
# pylint: disable=invalid-name, c-extension-no-member, unused-import
|
4
|
+
|
5
|
+
# Status: Active
|
6
|
+
# Geographic Focus: Europe
|
7
|
+
# Participation: Worldwide
|
8
|
+
# Mode: CW
|
9
|
+
# Bands: 160, 80, 40, 20, 15, 10m
|
10
|
+
# Classes: (see your national society rules)
|
11
|
+
# Exchange: RST + Serial No.
|
12
|
+
# E-mail logs to: (none)
|
13
|
+
# Upload log at: https://dxhf2.darc.de/~fdcwlog/upload.cgi?form=referat&lang=en
|
14
|
+
# Mail logs to: (none)
|
15
|
+
# Find rules at: https://www.darc.de/der-club/referate/conteste/iaru-region-1-fieldday/en/
|
16
|
+
|
17
|
+
# Scoring
|
18
|
+
# Fixed station to fixed station: 0 points
|
19
|
+
|
20
|
+
# For contacts with:
|
21
|
+
|
22
|
+
# fixed stations in Europe: 2 points
|
23
|
+
# fixed stations outside Europe: 3 points
|
24
|
+
# portable stations in Europe: 4 points
|
25
|
+
# portable stations outside Europe: 6 points
|
26
|
+
|
27
|
+
# Each station may be contacted only once per band.
|
28
|
+
|
29
|
+
# Portable stations only count as portable if they sign "/p", "/m", "/mm", or "/am",
|
30
|
+
|
31
|
+
# 10. Multipliers
|
32
|
+
# ONE for each WAE country / DXCC entity worked on each band
|
33
|
+
|
34
|
+
|
35
|
+
import datetime
|
36
|
+
import logging
|
37
|
+
|
38
|
+
from pathlib import Path
|
39
|
+
|
40
|
+
from PyQt6 import QtWidgets
|
41
|
+
|
42
|
+
from not1mm.lib.plugin_common import gen_adif, get_points
|
43
|
+
from not1mm.lib.version import __version__
|
44
|
+
|
45
|
+
logger = logging.getLogger(__name__)
|
46
|
+
|
47
|
+
EXCHANGE_HINT = "#"
|
48
|
+
|
49
|
+
name = "IARU FIELDDAY R1 DARC CW"
|
50
|
+
cabrillo_name = "IARU-FD-R1-DARC-CW"
|
51
|
+
mode = "CW" # CW SSB BOTH RTTY
|
52
|
+
# columns = [0, 1, 2, 3, 4, 5, 6, 9, 11, 15]
|
53
|
+
columns = [
|
54
|
+
"YYYY-MM-DD HH:MM:SS",
|
55
|
+
"Call",
|
56
|
+
"Freq",
|
57
|
+
"Snt",
|
58
|
+
"Rcv",
|
59
|
+
"SentNr",
|
60
|
+
"RcvNr",
|
61
|
+
"M1",
|
62
|
+
"PTS",
|
63
|
+
]
|
64
|
+
|
65
|
+
advance_on_space = [True, True, True, True, True]
|
66
|
+
|
67
|
+
# 1 once per contest, 2 work each band, 3 each band/mode, 4 no dupe checking
|
68
|
+
dupe_type = 2
|
69
|
+
|
70
|
+
|
71
|
+
def init_contest(self):
|
72
|
+
"""setup plugin"""
|
73
|
+
set_tab_next(self)
|
74
|
+
set_tab_prev(self)
|
75
|
+
interface(self)
|
76
|
+
self.next_field = self.other_2
|
77
|
+
|
78
|
+
|
79
|
+
def interface(self):
|
80
|
+
"""Setup user interface"""
|
81
|
+
self.field1.show()
|
82
|
+
self.field2.show()
|
83
|
+
self.field3.show()
|
84
|
+
self.field4.show()
|
85
|
+
self.snt_label.setText("SNT")
|
86
|
+
self.field1.setAccessibleName("RST Sent")
|
87
|
+
self.other_label.setText("SentNR")
|
88
|
+
self.field3.setAccessibleName("Sent Number")
|
89
|
+
self.exch_label.setText("RcvNR")
|
90
|
+
self.field4.setAccessibleName("Received Number")
|
91
|
+
|
92
|
+
|
93
|
+
def reset_label(self): # pylint: disable=unused-argument
|
94
|
+
"""reset label after field cleared"""
|
95
|
+
|
96
|
+
|
97
|
+
def set_tab_next(self):
|
98
|
+
"""Set TAB Advances"""
|
99
|
+
self.tab_next = {
|
100
|
+
self.callsign: self.field1.findChild(QtWidgets.QLineEdit),
|
101
|
+
self.field1.findChild(QtWidgets.QLineEdit): self.field2.findChild(
|
102
|
+
QtWidgets.QLineEdit
|
103
|
+
),
|
104
|
+
self.field2.findChild(QtWidgets.QLineEdit): self.field3.findChild(
|
105
|
+
QtWidgets.QLineEdit
|
106
|
+
),
|
107
|
+
self.field3.findChild(QtWidgets.QLineEdit): self.field4.findChild(
|
108
|
+
QtWidgets.QLineEdit
|
109
|
+
),
|
110
|
+
self.field4.findChild(QtWidgets.QLineEdit): self.callsign,
|
111
|
+
}
|
112
|
+
|
113
|
+
|
114
|
+
def set_tab_prev(self):
|
115
|
+
"""Set TAB Advances"""
|
116
|
+
self.tab_prev = {
|
117
|
+
self.callsign: self.field4.findChild(QtWidgets.QLineEdit),
|
118
|
+
self.field1.findChild(QtWidgets.QLineEdit): self.callsign,
|
119
|
+
self.field2.findChild(QtWidgets.QLineEdit): self.field1.findChild(
|
120
|
+
QtWidgets.QLineEdit
|
121
|
+
),
|
122
|
+
self.field3.findChild(QtWidgets.QLineEdit): self.field2.findChild(
|
123
|
+
QtWidgets.QLineEdit
|
124
|
+
),
|
125
|
+
self.field4.findChild(QtWidgets.QLineEdit): self.field3.findChild(
|
126
|
+
QtWidgets.QLineEdit
|
127
|
+
),
|
128
|
+
}
|
129
|
+
|
130
|
+
|
131
|
+
def set_contact_vars(self):
|
132
|
+
"""Contest Specific"""
|
133
|
+
self.contact["SNT"] = self.sent.text()
|
134
|
+
self.contact["RCV"] = self.receive.text()
|
135
|
+
self.contact["SentNr"] = self.other_1.text()
|
136
|
+
self.contact["NR"] = self.other_2.text()
|
137
|
+
if self.contact.get("CountryPrefix", ""):
|
138
|
+
result = self.database.fetch_dxcc_exists(self.contact.get("CountryPrefix", ""))
|
139
|
+
if result.get("dxcc_count", ""):
|
140
|
+
self.contact["IsMultiplier1"] = 0
|
141
|
+
else:
|
142
|
+
self.contact["IsMultiplier1"] = 1
|
143
|
+
|
144
|
+
|
145
|
+
def predupe(self): # pylint: disable=unused-argument
|
146
|
+
"""called after callsign entered"""
|
147
|
+
|
148
|
+
|
149
|
+
def prefill(self):
|
150
|
+
"""Fill SentNR"""
|
151
|
+
result = self.database.get_serial()
|
152
|
+
serial_nr = str(result.get("serial_nr", "1")).zfill(3)
|
153
|
+
if serial_nr == "None":
|
154
|
+
serial_nr = "001"
|
155
|
+
field = self.field3.findChild(QtWidgets.QLineEdit)
|
156
|
+
if len(field.text()) == 0:
|
157
|
+
field.setText(serial_nr)
|
158
|
+
|
159
|
+
|
160
|
+
def points(self):
|
161
|
+
"""
|
162
|
+
Calc point
|
163
|
+
|
164
|
+
# fixed stations in Europe: 2 points
|
165
|
+
# fixed stations outside Europe: 3 points
|
166
|
+
# portable stations in Europe: 4 points
|
167
|
+
# portable stations outside Europe: 6 points
|
168
|
+
"""
|
169
|
+
|
170
|
+
their_portable = False
|
171
|
+
im_portable = False
|
172
|
+
their_continent = self.contact.get("Continent", "")
|
173
|
+
the_call = self.contact.get("Call", "").upper()
|
174
|
+
|
175
|
+
if the_call.endswith(("/P", "/M", "/MM", "/AM")):
|
176
|
+
their_portable = True
|
177
|
+
if self.station.get("Call", "").endswith(("/P", "/M", "/MM", "/AM")):
|
178
|
+
im_portable = True
|
179
|
+
|
180
|
+
if not im_portable and not their_portable:
|
181
|
+
return 0
|
182
|
+
|
183
|
+
if their_continent:
|
184
|
+
if their_portable:
|
185
|
+
if their_continent == "EU":
|
186
|
+
return 4
|
187
|
+
else:
|
188
|
+
return 6
|
189
|
+
else:
|
190
|
+
if their_continent == "EU":
|
191
|
+
return 2
|
192
|
+
else:
|
193
|
+
return 3
|
194
|
+
|
195
|
+
# Something wrong
|
196
|
+
return 0
|
197
|
+
|
198
|
+
|
199
|
+
def show_mults(self):
|
200
|
+
"""Return display string for mults"""
|
201
|
+
result = self.database.fetch_mult_count(1)
|
202
|
+
if result:
|
203
|
+
return int(result.get("count", 0))
|
204
|
+
return 0
|
205
|
+
|
206
|
+
|
207
|
+
def show_qso(self):
|
208
|
+
"""Return qso count"""
|
209
|
+
result = self.database.fetch_qso_count()
|
210
|
+
if result:
|
211
|
+
return int(result.get("qsos", 0))
|
212
|
+
return 0
|
213
|
+
|
214
|
+
|
215
|
+
def calc_score(self):
|
216
|
+
"""Return calculated score"""
|
217
|
+
result = self.database.fetch_points()
|
218
|
+
if result is not None:
|
219
|
+
score = result.get("Points", "0")
|
220
|
+
if score is None:
|
221
|
+
score = "0"
|
222
|
+
contest_points = int(score)
|
223
|
+
result = self.database.fetch_mult_count(1)
|
224
|
+
mults = int(result.get("count", 0))
|
225
|
+
return contest_points * mults
|
226
|
+
return 0
|
227
|
+
|
228
|
+
|
229
|
+
def adif(self):
|
230
|
+
"""Call the generate ADIF function"""
|
231
|
+
gen_adif(self, cabrillo_name, "IARU FIELDDAY R1 DARC CW")
|
232
|
+
|
233
|
+
|
234
|
+
def cabrillo(self):
|
235
|
+
"""Generates Cabrillo file. Maybe."""
|
236
|
+
# https://www.cqwpx.com/cabrillo.htm
|
237
|
+
logger.debug("******Cabrillo*****")
|
238
|
+
logger.debug("Station: %s", f"{self.station}")
|
239
|
+
logger.debug("Contest: %s", f"{self.contest_settings}")
|
240
|
+
now = datetime.datetime.now()
|
241
|
+
date_time = now.strftime("%Y-%m-%d_%H-%M-%S")
|
242
|
+
filename = (
|
243
|
+
str(Path.home())
|
244
|
+
+ "/"
|
245
|
+
+ f"{self.station.get('Call', '').upper()}_{cabrillo_name}_{date_time}.log"
|
246
|
+
)
|
247
|
+
logger.debug("%s", filename)
|
248
|
+
log = self.database.fetch_all_contacts_asc()
|
249
|
+
try:
|
250
|
+
with open(filename, "w", encoding="ascii") as file_descriptor:
|
251
|
+
print("START-OF-LOG: 3.0", end="\r\n", file=file_descriptor)
|
252
|
+
print(
|
253
|
+
f"CREATED-BY: Not1MM v{__version__}",
|
254
|
+
end="\r\n",
|
255
|
+
file=file_descriptor,
|
256
|
+
)
|
257
|
+
print(
|
258
|
+
f"CONTEST: {cabrillo_name}",
|
259
|
+
end="\r\n",
|
260
|
+
file=file_descriptor,
|
261
|
+
)
|
262
|
+
if self.station.get("Club", ""):
|
263
|
+
print(
|
264
|
+
f"CLUB: {self.station.get('Club', '').upper()}",
|
265
|
+
end="\r\n",
|
266
|
+
file=file_descriptor,
|
267
|
+
)
|
268
|
+
print(
|
269
|
+
f"CALLSIGN: {self.station.get('Call','')}",
|
270
|
+
end="\r\n",
|
271
|
+
file=file_descriptor,
|
272
|
+
)
|
273
|
+
print(
|
274
|
+
f"LOCATION: {self.station.get('ARRLSection', '')}",
|
275
|
+
end="\r\n",
|
276
|
+
file=file_descriptor,
|
277
|
+
)
|
278
|
+
# print(
|
279
|
+
# f"ARRL-SECTION: {self.pref.get('section', '')}",
|
280
|
+
# end="\r\n",
|
281
|
+
# file=file_descriptor,
|
282
|
+
# )
|
283
|
+
print(
|
284
|
+
f"CATEGORY-OPERATOR: {self.contest_settings.get('OperatorCategory','')}",
|
285
|
+
end="\r\n",
|
286
|
+
file=file_descriptor,
|
287
|
+
)
|
288
|
+
print(
|
289
|
+
f"CATEGORY-ASSISTED: {self.contest_settings.get('AssistedCategory','')}",
|
290
|
+
end="\r\n",
|
291
|
+
file=file_descriptor,
|
292
|
+
)
|
293
|
+
print(
|
294
|
+
f"CATEGORY-BAND: {self.contest_settings.get('BandCategory','')}",
|
295
|
+
end="\r\n",
|
296
|
+
file=file_descriptor,
|
297
|
+
)
|
298
|
+
print(
|
299
|
+
f"CATEGORY-MODE: {self.contest_settings.get('ModeCategory','')}",
|
300
|
+
end="\r\n",
|
301
|
+
file=file_descriptor,
|
302
|
+
)
|
303
|
+
print(
|
304
|
+
f"CATEGORY-TRANSMITTER: {self.contest_settings.get('TransmitterCategory','')}",
|
305
|
+
end="\r\n",
|
306
|
+
file=file_descriptor,
|
307
|
+
)
|
308
|
+
if self.contest_settings.get("OverlayCategory", "") != "N/A":
|
309
|
+
print(
|
310
|
+
f"CATEGORY-OVERLAY: {self.contest_settings.get('OverlayCategory','')}",
|
311
|
+
end="\r\n",
|
312
|
+
file=file_descriptor,
|
313
|
+
)
|
314
|
+
print(
|
315
|
+
f"GRID-LOCATOR: {self.station.get('GridSquare','')}",
|
316
|
+
end="\r\n",
|
317
|
+
file=file_descriptor,
|
318
|
+
)
|
319
|
+
# print(
|
320
|
+
# f"CATEGORY: {None}",
|
321
|
+
# end="\r\n",
|
322
|
+
# file=file_descriptor,
|
323
|
+
# )
|
324
|
+
print(
|
325
|
+
f"CATEGORY-POWER: {self.contest_settings.get('PowerCategory','')}",
|
326
|
+
end="\r\n",
|
327
|
+
file=file_descriptor,
|
328
|
+
)
|
329
|
+
|
330
|
+
print(
|
331
|
+
f"CLAIMED-SCORE: {calc_score(self)}",
|
332
|
+
end="\r\n",
|
333
|
+
file=file_descriptor,
|
334
|
+
)
|
335
|
+
ops = f"@{self.station.get('Call','')}"
|
336
|
+
list_of_ops = self.database.get_ops()
|
337
|
+
for op in list_of_ops:
|
338
|
+
ops += f", {op.get('Operator', '')}"
|
339
|
+
print(
|
340
|
+
f"OPERATORS: {ops}",
|
341
|
+
end="\r\n",
|
342
|
+
file=file_descriptor,
|
343
|
+
)
|
344
|
+
print(
|
345
|
+
f"NAME: {self.station.get('Name', '')}",
|
346
|
+
end="\r\n",
|
347
|
+
file=file_descriptor,
|
348
|
+
)
|
349
|
+
print(
|
350
|
+
f"ADDRESS: {self.station.get('Street1', '')}",
|
351
|
+
end="\r\n",
|
352
|
+
file=file_descriptor,
|
353
|
+
)
|
354
|
+
print(
|
355
|
+
f"ADDRESS-CITY: {self.station.get('City', '')}",
|
356
|
+
end="\r\n",
|
357
|
+
file=file_descriptor,
|
358
|
+
)
|
359
|
+
print(
|
360
|
+
f"ADDRESS-STATE-PROVINCE: {self.station.get('State', '')}",
|
361
|
+
end="\r\n",
|
362
|
+
file=file_descriptor,
|
363
|
+
)
|
364
|
+
print(
|
365
|
+
f"ADDRESS-POSTALCODE: {self.station.get('Zip', '')}",
|
366
|
+
end="\r\n",
|
367
|
+
file=file_descriptor,
|
368
|
+
)
|
369
|
+
print(
|
370
|
+
f"ADDRESS-COUNTRY: {self.station.get('Country', '')}",
|
371
|
+
end="\r\n",
|
372
|
+
file=file_descriptor,
|
373
|
+
)
|
374
|
+
print(
|
375
|
+
f"EMAIL: {self.station.get('Email', '')}",
|
376
|
+
end="\r\n",
|
377
|
+
file=file_descriptor,
|
378
|
+
)
|
379
|
+
for contact in log:
|
380
|
+
the_date_and_time = contact.get("TS", "")
|
381
|
+
themode = contact.get("Mode", "")
|
382
|
+
if themode == "LSB" or themode == "USB":
|
383
|
+
themode = "PH"
|
384
|
+
frequency = str(int(contact.get("Freq", "0"))).rjust(5)
|
385
|
+
|
386
|
+
loggeddate = the_date_and_time[:10]
|
387
|
+
loggedtime = the_date_and_time[11:13] + the_date_and_time[14:16]
|
388
|
+
print(
|
389
|
+
f"QSO: {frequency} {themode} {loggeddate} {loggedtime} "
|
390
|
+
f"{contact.get('StationPrefix', '').ljust(13)} "
|
391
|
+
f"{str(contact.get('SNT', '')).ljust(3)} "
|
392
|
+
f"{str(contact.get('SentNr', '')).ljust(6)} "
|
393
|
+
f"{contact.get('Call', '').ljust(13)} "
|
394
|
+
f"{str(contact.get('RCV', '')).ljust(3)} "
|
395
|
+
f"{str(contact.get('NR', '')).ljust(6)}",
|
396
|
+
end="\r\n",
|
397
|
+
file=file_descriptor,
|
398
|
+
)
|
399
|
+
print("END-OF-LOG:", end="\r\n", file=file_descriptor)
|
400
|
+
self.show_message_box(f"Cabrillo saved to: {filename}")
|
401
|
+
except IOError as exception:
|
402
|
+
logger.critical("cabrillo: IO error: %s, writing to %s", exception, filename)
|
403
|
+
self.show_message_box(f"Error saving Cabrillo: {exception} {filename}")
|
404
|
+
return
|
405
|
+
|
406
|
+
|
407
|
+
def recalculate_mults(self):
|
408
|
+
"""Recalculates multipliers after change in logged qso."""
|
409
|
+
all_contacts = self.database.fetch_all_contacts_asc()
|
410
|
+
for contact in all_contacts:
|
411
|
+
self.contact = contact
|
412
|
+
contact["Points"] = points(self)
|
413
|
+
time_stamp = contact.get("TS", "")
|
414
|
+
dxcc = contact.get("CountryPrefix", "")
|
415
|
+
result = self.database.fetch_dxcc_exists_before_me(dxcc, time_stamp)
|
416
|
+
dxcc_count = result.get("dxcc_count", 1)
|
417
|
+
if dxcc_count == 0:
|
418
|
+
contact["IsMultiplier1"] = 1
|
419
|
+
else:
|
420
|
+
contact["IsMultiplier1"] = 0
|
421
|
+
self.database.change_contact(contact)
|