not1mm 24.8.27__py3-none-any.whl → 24.9.5__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 +108 -54
- not1mm/lib/database.py +2 -16
- not1mm/lib/fldigi_watcher.py +32 -0
- not1mm/lib/ft8_watcher.py +5 -1
- not1mm/lib/version.py +1 -1
- not1mm/plugins/arrl_field_day.py +43 -8
- not1mm/plugins/cq_160_cw.py +2 -2
- not1mm/plugins/cq_160_ssb.py +2 -2
- not1mm/plugins/helvetia.py +2 -2
- not1mm/plugins/ref_cw.py +502 -0
- not1mm/test.py +5 -12
- {not1mm-24.8.27.dist-info → not1mm-24.9.5.dist-info}/METADATA +13 -10
- {not1mm-24.8.27.dist-info → not1mm-24.9.5.dist-info}/RECORD +17 -15
- {not1mm-24.8.27.dist-info → not1mm-24.9.5.dist-info}/WHEEL +1 -1
- {not1mm-24.8.27.dist-info → not1mm-24.9.5.dist-info}/LICENSE +0 -0
- {not1mm-24.8.27.dist-info → not1mm-24.9.5.dist-info}/entry_points.txt +0 -0
- {not1mm-24.8.27.dist-info → not1mm-24.9.5.dist-info}/top_level.txt +0 -0
not1mm/__main__.py
CHANGED
@@ -23,6 +23,7 @@ import threading
|
|
23
23
|
import uuid
|
24
24
|
|
25
25
|
from json import dumps, loads
|
26
|
+
from json.decoder import JSONDecodeError
|
26
27
|
from pathlib import Path
|
27
28
|
from shutil import copyfile
|
28
29
|
|
@@ -66,6 +67,7 @@ from not1mm.lib.settings import Settings
|
|
66
67
|
from not1mm.lib.version import __version__
|
67
68
|
from not1mm.lib.versiontest import VersionTest
|
68
69
|
from not1mm.lib.ft8_watcher import FT8Watcher
|
70
|
+
from not1mm.lib.fldigi_watcher import FlDigiWatcher
|
69
71
|
|
70
72
|
import not1mm.fsutils as fsutils
|
71
73
|
from not1mm.logwindow import LogWindow
|
@@ -162,7 +164,9 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
162
164
|
|
163
165
|
radio_thread = QThread()
|
164
166
|
voice_thread = QThread()
|
167
|
+
fldigi_thread = QThread()
|
165
168
|
|
169
|
+
fldigi_watcher = None
|
166
170
|
rig_control = None
|
167
171
|
log_window = None
|
168
172
|
check_window = None
|
@@ -186,14 +190,13 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
186
190
|
)
|
187
191
|
self.setCorner(Qt.Corner.TopLeftCorner, Qt.DockWidgetArea.LeftDockWidgetArea)
|
188
192
|
self.setCorner(Qt.Corner.BottomLeftCorner, Qt.DockWidgetArea.LeftDockWidgetArea)
|
189
|
-
|
190
|
-
uic.loadUi(data_path, self)
|
193
|
+
uic.loadUi(fsutils.APP_DATA_PATH / "main.ui", self)
|
191
194
|
self.cw_entry.hide()
|
192
195
|
self.leftdot.hide()
|
193
196
|
self.rightdot.hide()
|
194
197
|
self.n1mm = N1MM()
|
195
198
|
self.ft8 = FT8Watcher()
|
196
|
-
self.ft8.set_callback(
|
199
|
+
self.ft8.set_callback(None)
|
197
200
|
self.mscp = SCP(fsutils.APP_DATA_PATH)
|
198
201
|
self.next_field = self.other_2
|
199
202
|
self.dupe_indicator.hide()
|
@@ -459,8 +462,15 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
459
462
|
self.setWindowIcon(
|
460
463
|
QtGui.QIcon(str(fsutils.APP_DATA_PATH / "k6gte.not1mm-32.png"))
|
461
464
|
)
|
462
|
-
|
463
|
-
|
465
|
+
|
466
|
+
try:
|
467
|
+
with open(
|
468
|
+
fsutils.APP_DATA_PATH / "cty.json", "rt", encoding="utf-8"
|
469
|
+
) as c_file:
|
470
|
+
self.ctyfile = loads(c_file.read())
|
471
|
+
except (IOError, JSONDecodeError, TypeError):
|
472
|
+
logging.CRITICAL("There was an error parsing the BigCity file.")
|
473
|
+
|
464
474
|
self.readpreferences()
|
465
475
|
|
466
476
|
self.voice_process = Voice()
|
@@ -491,9 +501,7 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
491
501
|
self.make_op_dir()
|
492
502
|
|
493
503
|
self.clearinputs()
|
494
|
-
|
495
|
-
if self.pref.get("contest"):
|
496
|
-
self.load_contest()
|
504
|
+
self.load_contest()
|
497
505
|
self.read_cw_macros()
|
498
506
|
|
499
507
|
# Featureset for wayland
|
@@ -502,6 +510,13 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
502
510
|
| QtWidgets.QDockWidget.DockWidgetFeature.DockWidgetMovable
|
503
511
|
)
|
504
512
|
|
513
|
+
self.fldigi_watcher = FlDigiWatcher()
|
514
|
+
self.fldigi_watcher.moveToThread(self.fldigi_thread)
|
515
|
+
self.fldigi_thread.started.connect(self.fldigi_watcher.run)
|
516
|
+
self.fldigi_thread.finished.connect(self.fldigi_watcher.deleteLater)
|
517
|
+
self.fldigi_watcher.poll_callback.connect(self.fldigi_qso)
|
518
|
+
self.fldigi_thread.start()
|
519
|
+
|
505
520
|
self.log_window = LogWindow()
|
506
521
|
self.log_window.setObjectName("log-window")
|
507
522
|
if os.environ.get("WAYLAND_DISPLAY"):
|
@@ -561,32 +576,50 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
561
576
|
"You can udate to the current version by using:\npip install -U not1mm"
|
562
577
|
)
|
563
578
|
|
564
|
-
def
|
579
|
+
def fldigi_qso(self, result: str):
|
565
580
|
"""
|
566
|
-
|
581
|
+
gets called when there is a new fldigi qso logged.
|
567
582
|
|
568
583
|
{
|
569
|
-
'
|
570
|
-
'
|
571
|
-
'MODE': '
|
572
|
-
'
|
573
|
-
'
|
574
|
-
'
|
575
|
-
'
|
576
|
-
'
|
577
|
-
'
|
578
|
-
'
|
579
|
-
'
|
584
|
+
'FREQ': '7.029500',
|
585
|
+
'CALL': 'DL2DSL',
|
586
|
+
'MODE': 'RTTY',
|
587
|
+
'NAME': 'BOB',
|
588
|
+
'QSO_DATE': '20240904',
|
589
|
+
'QSO_DATE_OFF': '20240904',
|
590
|
+
'TIME_OFF': '212825',
|
591
|
+
'TIME_ON': '212800',
|
592
|
+
'RST_RCVD': '599',
|
593
|
+
'RST_SENT': '599',
|
594
|
+
'BAND': '40M',
|
595
|
+
'COUNTRY': 'FED. REP. OF GERMANY',
|
596
|
+
'CQZ': '14',
|
597
|
+
'STX': '000',
|
598
|
+
'STX_STRING': '1D ORG',
|
599
|
+
'CLASS': '1D',
|
600
|
+
'ARRL_SECT': 'DX',
|
601
|
+
'TX_PWR': '0',
|
602
|
+
'OPERATOR': 'K6GTE',
|
580
603
|
'STATION_CALLSIGN': 'K6GTE',
|
581
604
|
'MY_GRIDSQUARE': 'DM13AT',
|
582
|
-
'
|
583
|
-
'
|
584
|
-
'CLASS': '1D',
|
585
|
-
'ARRL_SECT': 'UT'
|
605
|
+
'MY_CITY': 'ANAHEIM, CA',
|
606
|
+
'MY_STATE': 'CA'
|
586
607
|
}
|
587
608
|
|
588
609
|
"""
|
589
|
-
|
610
|
+
|
611
|
+
datadict = {}
|
612
|
+
splitdata = result.upper().strip().split("<")
|
613
|
+
for data in splitdata:
|
614
|
+
if data:
|
615
|
+
tag = data.split(":")
|
616
|
+
if tag == ["EOR>"]:
|
617
|
+
break
|
618
|
+
datadict[tag[0]] = tag[1].split(">")[1].strip()
|
619
|
+
logger.debug(f"{datadict=}")
|
620
|
+
if hasattr(self.contest, "ft8_handler"):
|
621
|
+
self.contest.set_self(self)
|
622
|
+
self.contest.ft8_handler(datadict)
|
590
623
|
|
591
624
|
def setDarkMode(self, setdarkmode=False) -> None:
|
592
625
|
"""Forces a darkmode palette."""
|
@@ -1287,10 +1320,15 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
1287
1320
|
if updated:
|
1288
1321
|
cty.dump(fsutils.APP_DATA_PATH / "cty.json")
|
1289
1322
|
self.show_message_box("cty file updated.")
|
1290
|
-
|
1291
|
-
|
1292
|
-
|
1293
|
-
|
1323
|
+
try:
|
1324
|
+
with open(
|
1325
|
+
fsutils.APP_DATA_PATH / "cty.json", "rt", encoding="utf-8"
|
1326
|
+
) as ctyfile:
|
1327
|
+
self.ctyfile = loads(ctyfile.read())
|
1328
|
+
except (IOError, JSONDecodeError, TypeError) as err:
|
1329
|
+
logging.CRITICAL(
|
1330
|
+
f"There was an error {err} parsing the BigCity file."
|
1331
|
+
)
|
1294
1332
|
else:
|
1295
1333
|
self.show_message_box("An Error occured updating file.")
|
1296
1334
|
else:
|
@@ -1898,11 +1936,14 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
1898
1936
|
" "
|
1899
1937
|
)[:19]
|
1900
1938
|
self.contact["Call"] = self.callsign.text()
|
1901
|
-
self.contact
|
1902
|
-
|
1903
|
-
|
1904
|
-
|
1905
|
-
|
1939
|
+
if self.contact.get("Mode") not in ("FT8", "FT4"):
|
1940
|
+
self.contact["Freq"] = round(
|
1941
|
+
float(self.radio_state.get("vfoa", 0.0)) / 1000, 2
|
1942
|
+
)
|
1943
|
+
self.contact["QSXFreq"] = round(
|
1944
|
+
float(self.radio_state.get("vfoa", 0.0)) / 1000, 2
|
1945
|
+
)
|
1946
|
+
self.contact["Mode"] = self.radio_state.get("mode", "")
|
1906
1947
|
self.contact["ContestName"] = self.contest.cabrillo_name
|
1907
1948
|
self.contact["ContestNR"] = self.pref.get("contest", "0")
|
1908
1949
|
self.contact["StationPrefix"] = self.station.get("Call", "")
|
@@ -2093,7 +2134,7 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
2093
2134
|
|
2094
2135
|
def save_settings(self) -> None:
|
2095
2136
|
"""
|
2096
|
-
Save settings to database.
|
2137
|
+
Save Station settings to database.
|
2097
2138
|
|
2098
2139
|
Parameters
|
2099
2140
|
----------
|
@@ -2322,7 +2363,7 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
2322
2363
|
with open(fsutils.CONFIG_FILE, "wt", encoding="utf-8") as file_descriptor:
|
2323
2364
|
file_descriptor.write(dumps(self.pref, indent=4))
|
2324
2365
|
# logger.info("writing: %s", self.pref)
|
2325
|
-
except IOError as exception:
|
2366
|
+
except (IOError, TypeError, ValueError) as exception:
|
2326
2367
|
logger.critical("writepreferences: %s", exception)
|
2327
2368
|
|
2328
2369
|
def readpreferences(self) -> None:
|
@@ -2344,7 +2385,12 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
2344
2385
|
with open(
|
2345
2386
|
fsutils.CONFIG_FILE, "rt", encoding="utf-8"
|
2346
2387
|
) as file_descriptor:
|
2347
|
-
|
2388
|
+
try:
|
2389
|
+
self.pref = loads(file_descriptor.read())
|
2390
|
+
except (JSONDecodeError, TypeError):
|
2391
|
+
logging.CRITICAL(
|
2392
|
+
"There was an error parsing the preference file."
|
2393
|
+
)
|
2348
2394
|
logger.info("%s", self.pref)
|
2349
2395
|
else:
|
2350
2396
|
logger.info("No preference file. Writing preference.")
|
@@ -2354,7 +2400,7 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
2354
2400
|
self.pref = self.pref_ref.copy()
|
2355
2401
|
file_descriptor.write(dumps(self.pref, indent=4))
|
2356
2402
|
logger.info("%s", self.pref)
|
2357
|
-
except IOError as exception:
|
2403
|
+
except (IOError, TypeError, ValueError) as exception:
|
2358
2404
|
logger.critical("Error: %s", exception)
|
2359
2405
|
|
2360
2406
|
self.look_up = None
|
@@ -3197,10 +3243,14 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
3197
3243
|
else:
|
3198
3244
|
macro_file = "ssbmacros.txt"
|
3199
3245
|
if not (fsutils.USER_DATA_PATH / macro_file).exists():
|
3200
|
-
logger.debug("
|
3201
|
-
|
3202
|
-
|
3203
|
-
|
3246
|
+
logger.debug("copying default macro file.")
|
3247
|
+
try:
|
3248
|
+
copyfile(
|
3249
|
+
fsutils.APP_DATA_PATH / macro_file,
|
3250
|
+
fsutils.USER_DATA_PATH / macro_file,
|
3251
|
+
)
|
3252
|
+
except IOError as err:
|
3253
|
+
logger.critical(f"Error {err} copying macro file.")
|
3204
3254
|
try:
|
3205
3255
|
fsutils.openFileWithOS(fsutils.USER_DATA_PATH / macro_file)
|
3206
3256
|
except FileNotFoundError | PermissionError | OSError as err:
|
@@ -3221,22 +3271,26 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
3221
3271
|
macro_file = "ssbmacros.txt"
|
3222
3272
|
|
3223
3273
|
if not (fsutils.USER_DATA_PATH / macro_file).exists():
|
3224
|
-
logger.debug("
|
3225
|
-
|
3226
|
-
|
3227
|
-
|
3228
|
-
|
3229
|
-
|
3230
|
-
|
3231
|
-
|
3232
|
-
|
3274
|
+
logger.debug("copying default macro file.")
|
3275
|
+
try:
|
3276
|
+
copyfile(
|
3277
|
+
fsutils.APP_DATA_PATH / macro_file,
|
3278
|
+
fsutils.USER_DATA_PATH / macro_file,
|
3279
|
+
)
|
3280
|
+
except IOError as err:
|
3281
|
+
logger.critical(f"Error {err} copying macro file.")
|
3282
|
+
try:
|
3283
|
+
with open(
|
3284
|
+
fsutils.USER_DATA_PATH / macro_file, "r", encoding="utf-8"
|
3285
|
+
) as file_descriptor:
|
3286
|
+
for line in file_descriptor:
|
3233
3287
|
mode, fkey, buttonname, cwtext = line.split("|")
|
3234
3288
|
if mode.strip().upper() == "R" and self.pref.get("run_state"):
|
3235
3289
|
self.fkeys[fkey.strip()] = (buttonname.strip(), cwtext.strip())
|
3236
3290
|
if mode.strip().upper() != "R" and not self.pref.get("run_state"):
|
3237
3291
|
self.fkeys[fkey.strip()] = (buttonname.strip(), cwtext.strip())
|
3238
|
-
|
3239
|
-
|
3292
|
+
except (IOError, ValueError) as err:
|
3293
|
+
logger.info("read_cw_macros: %s", err)
|
3240
3294
|
keys = self.fkeys.keys()
|
3241
3295
|
if "F1" in keys:
|
3242
3296
|
self.F1.setText(f"F1: {self.fkeys['F1'][0]}")
|
not1mm/lib/database.py
CHANGED
@@ -886,28 +886,14 @@ class DataBase:
|
|
886
886
|
logger.debug("%s", exception)
|
887
887
|
return {}
|
888
888
|
|
889
|
-
def
|
889
|
+
def fetch_mult_count(self, mult: int) -> dict:
|
890
890
|
"""return QSO count"""
|
891
891
|
try:
|
892
892
|
with sqlite3.connect(self.database) as conn:
|
893
893
|
conn.row_factory = self.row_factory
|
894
894
|
cursor = conn.cursor()
|
895
895
|
cursor.execute(
|
896
|
-
f"select count(*) as count from dxlog where
|
897
|
-
)
|
898
|
-
return cursor.fetchone()
|
899
|
-
except sqlite3.OperationalError as exception:
|
900
|
-
logger.debug("%s", exception)
|
901
|
-
return {}
|
902
|
-
|
903
|
-
def fetch_mult2_count(self) -> dict:
|
904
|
-
"""return QSO count"""
|
905
|
-
try:
|
906
|
-
with sqlite3.connect(self.database) as conn:
|
907
|
-
conn.row_factory = self.row_factory
|
908
|
-
cursor = conn.cursor()
|
909
|
-
cursor.execute(
|
910
|
-
f"select count(*) as count from dxlog where IsMultiplier2 = 1 and ContestNR = {self.current_contest};"
|
896
|
+
f"select count(*) as count from dxlog where IsMultiplier{mult} = 1 and ContestNR = {self.current_contest};"
|
911
897
|
)
|
912
898
|
return cursor.fetchone()
|
913
899
|
except sqlite3.OperationalError as exception:
|
@@ -0,0 +1,32 @@
|
|
1
|
+
import xmlrpc.client
|
2
|
+
from PyQt6.QtCore import QObject, pyqtSignal, QThread, QEventLoop
|
3
|
+
|
4
|
+
|
5
|
+
class FlDigiWatcher(QObject):
|
6
|
+
"""fldigi watcher"""
|
7
|
+
|
8
|
+
poll_callback = pyqtSignal(str)
|
9
|
+
time_to_quit = False
|
10
|
+
|
11
|
+
def __init__(self):
|
12
|
+
super().__init__()
|
13
|
+
...
|
14
|
+
|
15
|
+
self.target = "http://127.0.0.1:7362"
|
16
|
+
self.payload = ""
|
17
|
+
self.response = ""
|
18
|
+
|
19
|
+
def run(self):
|
20
|
+
while not self.time_to_quit:
|
21
|
+
try:
|
22
|
+
server = xmlrpc.client.ServerProxy(self.target)
|
23
|
+
self.response = server.logbook.last_record()
|
24
|
+
except OSError:
|
25
|
+
continue
|
26
|
+
if self.payload != self.response:
|
27
|
+
self.payload = self.response
|
28
|
+
try:
|
29
|
+
self.poll_callback.emit(self.payload)
|
30
|
+
except QEventLoop:
|
31
|
+
...
|
32
|
+
QThread.msleep(100)
|
not1mm/lib/ft8_watcher.py
CHANGED
@@ -6,10 +6,14 @@ https://github.com/mbridak/not1mm
|
|
6
6
|
GPL V3
|
7
7
|
"""
|
8
8
|
|
9
|
+
import logging
|
10
|
+
|
9
11
|
from PyQt6 import QtNetwork
|
10
12
|
|
11
13
|
import struct
|
12
14
|
|
15
|
+
logger = logging.getLogger(__name__)
|
16
|
+
|
13
17
|
|
14
18
|
class FT8Watcher:
|
15
19
|
"""Main Window"""
|
@@ -64,7 +68,7 @@ class FT8Watcher:
|
|
64
68
|
datagram, sender_host, sender_port_number = self.udp_socket.readDatagram(
|
65
69
|
self.udp_socket.pendingDatagramSize()
|
66
70
|
)
|
67
|
-
|
71
|
+
logger.debug(f"{datagram=}")
|
68
72
|
|
69
73
|
if datagram[0:4] != b"\xad\xbc\xcb\xda":
|
70
74
|
return # bail if no wsjt-x magic number
|
not1mm/lib/version.py
CHANGED
not1mm/plugins/arrl_field_day.py
CHANGED
@@ -8,6 +8,7 @@ import logging
|
|
8
8
|
from pathlib import Path
|
9
9
|
from PyQt6 import QtWidgets
|
10
10
|
|
11
|
+
from not1mm.lib.ham_utility import get_logged_band
|
11
12
|
from not1mm.lib.plugin_common import gen_adif, get_points
|
12
13
|
from not1mm.lib.version import __version__
|
13
14
|
|
@@ -114,7 +115,7 @@ def points(self):
|
|
114
115
|
_mode = self.contact.get("Mode", "")
|
115
116
|
if _mode in "SSB, USB, LSB, FM, AM":
|
116
117
|
return 1
|
117
|
-
if _mode in "CW, RTTY":
|
118
|
+
if _mode in "CW, RTTY, FT8":
|
118
119
|
return 2
|
119
120
|
return 0
|
120
121
|
|
@@ -295,8 +296,10 @@ def cabrillo(self):
|
|
295
296
|
for contact in log:
|
296
297
|
the_date_and_time = contact.get("TS", "")
|
297
298
|
themode = contact.get("Mode", "")
|
298
|
-
if themode
|
299
|
+
if themode in ("LSB", "USB", "FM"):
|
299
300
|
themode = "PH"
|
301
|
+
if themode in ("FT8", "FT4", "RTTY"):
|
302
|
+
themode = "DG"
|
300
303
|
frequency = str(int(contact.get("Freq", "0"))).rjust(5)
|
301
304
|
|
302
305
|
loggeddate = the_date_and_time[:10]
|
@@ -330,7 +333,7 @@ def set_self(the_outie):
|
|
330
333
|
|
331
334
|
def ft8_handler(the_packet: dict):
|
332
335
|
"""Process FT8 QSO packets
|
333
|
-
|
336
|
+
FT8
|
334
337
|
{
|
335
338
|
'CALL': 'KE0OG',
|
336
339
|
'GRIDSQUARE': 'DM10AT',
|
@@ -350,19 +353,51 @@ def ft8_handler(the_packet: dict):
|
|
350
353
|
'CLASS': '1D',
|
351
354
|
'ARRL_SECT': 'UT'
|
352
355
|
}
|
356
|
+
FlDigi
|
357
|
+
{
|
358
|
+
'FREQ': '7.029500',
|
359
|
+
'CALL': 'DL2DSL',
|
360
|
+
'MODE': 'RTTY',
|
361
|
+
'NAME': 'BOB',
|
362
|
+
'QSO_DATE': '20240904',
|
363
|
+
'QSO_DATE_OFF': '20240904',
|
364
|
+
'TIME_OFF': '212825',
|
365
|
+
'TIME_ON': '212800',
|
366
|
+
'RST_RCVD': '599',
|
367
|
+
'RST_SENT': '599',
|
368
|
+
'BAND': '40M',
|
369
|
+
'COUNTRY': 'FED. REP. OF GERMANY',
|
370
|
+
'CQZ': '14',
|
371
|
+
'STX': '000',
|
372
|
+
'STX_STRING': '1D ORG',
|
373
|
+
'CLASS': '1D',
|
374
|
+
'ARRL_SECT': 'DX',
|
375
|
+
'TX_PWR': '0',
|
376
|
+
'OPERATOR': 'K6GTE',
|
377
|
+
'STATION_CALLSIGN': 'K6GTE',
|
378
|
+
'MY_GRIDSQUARE': 'DM13AT',
|
379
|
+
'MY_CITY': 'ANAHEIM, CA',
|
380
|
+
'MY_STATE': 'CA'
|
381
|
+
}
|
353
382
|
|
354
383
|
"""
|
355
|
-
|
356
|
-
|
384
|
+
logger.debug(f"{the_packet=}")
|
357
385
|
if ALTEREGO is not None:
|
358
|
-
|
359
386
|
ALTEREGO.callsign.setText(the_packet.get("CALL"))
|
360
387
|
ALTEREGO.contact["Call"] = the_packet.get("CALL", "")
|
361
388
|
ALTEREGO.contact["SNT"] = ALTEREGO.sent.text()
|
362
389
|
ALTEREGO.contact["RCV"] = ALTEREGO.receive.text()
|
363
390
|
ALTEREGO.contact["Exchange1"] = the_packet.get("CLASS", "ERR")
|
364
391
|
ALTEREGO.contact["Sect"] = the_packet.get("ARRL_SECT", "ERR")
|
365
|
-
ALTEREGO.contact["Mode"] = "
|
392
|
+
ALTEREGO.contact["Mode"] = the_packet.get("MODE", "ERR")
|
393
|
+
ALTEREGO.contact["Freq"] = round(float(the_packet.get("FREQ", "0.0")) * 1000, 2)
|
394
|
+
ALTEREGO.contact["QSXFreq"] = round(
|
395
|
+
float(the_packet.get("FREQ", "0.0")) * 1000, 2
|
396
|
+
)
|
397
|
+
ALTEREGO.contact["Band"] = get_logged_band(
|
398
|
+
str(int(float(the_packet.get("FREQ", "0.0")) * 1000000))
|
399
|
+
)
|
400
|
+
logger.debug(f"{ALTEREGO.contact=}")
|
366
401
|
ALTEREGO.other_1.setText(the_packet.get("CLASS", "ERR"))
|
367
402
|
ALTEREGO.other_2.setText(the_packet.get("ARRL_SECT", "ERR"))
|
368
|
-
|
403
|
+
ALTEREGO.save_contact()
|
not1mm/plugins/cq_160_cw.py
CHANGED
@@ -177,7 +177,7 @@ def points(self):
|
|
177
177
|
|
178
178
|
def show_mults(self):
|
179
179
|
"""Return display string for mults"""
|
180
|
-
result = self.database.
|
180
|
+
result = self.database.fetch_mult_count(1)
|
181
181
|
count = result.get("count", 0)
|
182
182
|
return count
|
183
183
|
|
@@ -198,7 +198,7 @@ def calc_score(self):
|
|
198
198
|
if score is None:
|
199
199
|
score = "0"
|
200
200
|
contest_points = int(score)
|
201
|
-
result = self.database.
|
201
|
+
result = self.database.fetch_mult_count(1)
|
202
202
|
mults = int(result.get("count", 0))
|
203
203
|
return contest_points * mults
|
204
204
|
return 0
|
not1mm/plugins/cq_160_ssb.py
CHANGED
@@ -177,7 +177,7 @@ def points(self):
|
|
177
177
|
|
178
178
|
def show_mults(self):
|
179
179
|
"""Return display string for mults"""
|
180
|
-
result = self.database.
|
180
|
+
result = self.database.fetch_mult_count(1)
|
181
181
|
count = result.get("count", 0)
|
182
182
|
return count
|
183
183
|
|
@@ -198,7 +198,7 @@ def calc_score(self):
|
|
198
198
|
if score is None:
|
199
199
|
score = "0"
|
200
200
|
contest_points = int(score)
|
201
|
-
result = self.database.
|
201
|
+
result = self.database.fetch_mult_count(1)
|
202
202
|
mults = int(result.get("count", 0))
|
203
203
|
return contest_points * mults
|
204
204
|
return 0
|
not1mm/plugins/helvetia.py
CHANGED
@@ -250,8 +250,8 @@ def points(self):
|
|
250
250
|
|
251
251
|
def show_mults(self):
|
252
252
|
"""Return display string for mults"""
|
253
|
-
return int(self.database.
|
254
|
-
self.database.
|
253
|
+
return int(self.database.fetch_mult_count(1).get("count", 0)) + int(
|
254
|
+
self.database.fetch_mult_count(2).get("count", 0)
|
255
255
|
)
|
256
256
|
|
257
257
|
|
not1mm/plugins/ref_cw.py
ADDED
@@ -0,0 +1,502 @@
|
|
1
|
+
"""
|
2
|
+
REF Contest, CW
|
3
|
+
Status: Active
|
4
|
+
Geographic Focus: France + overseas territories
|
5
|
+
Participation: Worldwide
|
6
|
+
Awards: Worldwide
|
7
|
+
Mode: CW
|
8
|
+
Bands: 80, 40, 20, 15, 10m
|
9
|
+
Classes: Single Op All Band
|
10
|
+
Single Op Single Band
|
11
|
+
Multi-Single
|
12
|
+
Club
|
13
|
+
SWL
|
14
|
+
Max power: HP: >100 Watts
|
15
|
+
LP: 100 Watts
|
16
|
+
QRP: 5 Watts
|
17
|
+
|
18
|
+
Exchange: French: RST + Department/Prefix
|
19
|
+
non-French: RST + Serial No.
|
20
|
+
|
21
|
+
Work stations: Once per band
|
22
|
+
|
23
|
+
QSO Points: French: 6 points per QSO with French station same continent
|
24
|
+
French: 15 points per QSO with French station on different continent
|
25
|
+
French: 1 point per QSO with non-French station same continent
|
26
|
+
French: 2 points per QSO with non-French station on different continent
|
27
|
+
non-French: 1 point per QSO with French station same continent
|
28
|
+
non-French: 3 points per QSO with French station on different continent
|
29
|
+
|
30
|
+
Multipliers: French/Corsica departments once per band
|
31
|
+
French overseas prefixes once per band
|
32
|
+
non-French DXCC countries once per band (available only to French stations)
|
33
|
+
|
34
|
+
Score Calculation: Total score = total QSO points x total mults
|
35
|
+
|
36
|
+
Upload log at: https://concours.r-e-f.org/contest/logs/upload-form/
|
37
|
+
Find rules at: https://concours.r-e-f.org/reglements/actuels/reg_cdfhfdx.pdf
|
38
|
+
Cabrillo name: REF-CW
|
39
|
+
Cabrillo name aliases: REF
|
40
|
+
"""
|
41
|
+
|
42
|
+
import datetime
|
43
|
+
import logging
|
44
|
+
import platform
|
45
|
+
|
46
|
+
from pathlib import Path
|
47
|
+
|
48
|
+
from PyQt6 import QtWidgets
|
49
|
+
|
50
|
+
from not1mm.lib.plugin_common import gen_adif, get_points
|
51
|
+
|
52
|
+
from not1mm.lib.version import __version__
|
53
|
+
|
54
|
+
logger = logging.getLogger(__name__)
|
55
|
+
|
56
|
+
EXCHANGE_HINT = "Canton or #"
|
57
|
+
|
58
|
+
name = "French REF DX contest - CW"
|
59
|
+
cabrillo_name = "REF-CW"
|
60
|
+
mode = "CW" # CW SSB BOTH RTTY
|
61
|
+
|
62
|
+
columns = [
|
63
|
+
"YYYY-MM-DD HH:MM:SS",
|
64
|
+
"Call",
|
65
|
+
"Freq",
|
66
|
+
"Mode",
|
67
|
+
"Snt",
|
68
|
+
"Rcv",
|
69
|
+
"SentNr",
|
70
|
+
"RcvNr",
|
71
|
+
"M1",
|
72
|
+
"M2",
|
73
|
+
"PTS",
|
74
|
+
]
|
75
|
+
|
76
|
+
advance_on_space = [True, True, True, True, True]
|
77
|
+
|
78
|
+
# 1 once per contest, 2 work each band, 3 each band/mode, 4 no dupe checking
|
79
|
+
dupe_type = 2
|
80
|
+
|
81
|
+
|
82
|
+
def init_contest(self):
|
83
|
+
"""setup plugin"""
|
84
|
+
set_tab_next(self)
|
85
|
+
set_tab_prev(self)
|
86
|
+
interface(self)
|
87
|
+
self.next_field = self.other_2
|
88
|
+
|
89
|
+
|
90
|
+
def interface(self):
|
91
|
+
"""Setup user interface"""
|
92
|
+
self.field1.show()
|
93
|
+
self.field2.show()
|
94
|
+
self.field3.show()
|
95
|
+
self.field4.show()
|
96
|
+
label = self.field3.findChild(QtWidgets.QLabel)
|
97
|
+
label.setText("Sent")
|
98
|
+
self.field3.setAccessibleName("Sent")
|
99
|
+
label = self.field4.findChild(QtWidgets.QLabel)
|
100
|
+
label.setText("Dep/Pfx/SN")
|
101
|
+
self.field4.setAccessibleName("Department, Prefix or SN")
|
102
|
+
|
103
|
+
|
104
|
+
def reset_label(self):
|
105
|
+
"""reset label after field cleared"""
|
106
|
+
|
107
|
+
|
108
|
+
def set_tab_next(self):
|
109
|
+
"""Set TAB Advances"""
|
110
|
+
self.tab_next = {
|
111
|
+
self.callsign: self.field3.findChild(QtWidgets.QLineEdit),
|
112
|
+
self.field1.findChild(QtWidgets.QLineEdit): self.field3.findChild(
|
113
|
+
QtWidgets.QLineEdit
|
114
|
+
),
|
115
|
+
self.field2.findChild(QtWidgets.QLineEdit): self.field3.findChild(
|
116
|
+
QtWidgets.QLineEdit
|
117
|
+
),
|
118
|
+
self.field3.findChild(QtWidgets.QLineEdit): self.field4.findChild(
|
119
|
+
QtWidgets.QLineEdit
|
120
|
+
),
|
121
|
+
self.field4.findChild(QtWidgets.QLineEdit): self.callsign,
|
122
|
+
}
|
123
|
+
|
124
|
+
|
125
|
+
def set_tab_prev(self):
|
126
|
+
"""Set TAB Advances"""
|
127
|
+
self.tab_prev = {
|
128
|
+
self.callsign: self.field4.findChild(QtWidgets.QLineEdit),
|
129
|
+
self.field1.findChild(QtWidgets.QLineEdit): self.callsign,
|
130
|
+
self.field2.findChild(QtWidgets.QLineEdit): self.callsign,
|
131
|
+
self.field3.findChild(QtWidgets.QLineEdit): self.callsign,
|
132
|
+
self.field4.findChild(QtWidgets.QLineEdit): self.field3.findChild(
|
133
|
+
QtWidgets.QLineEdit
|
134
|
+
),
|
135
|
+
}
|
136
|
+
|
137
|
+
|
138
|
+
def set_contact_vars(self):
|
139
|
+
"""
|
140
|
+
Contest Specific
|
141
|
+
Multipliers:
|
142
|
+
French/Corsica departments once per band
|
143
|
+
French overseas prefixes once per band
|
144
|
+
non-French DXCC countries once per band (available only to French stations)
|
145
|
+
"""
|
146
|
+
self.contact["SNT"] = self.sent.text()
|
147
|
+
self.contact["RCV"] = self.receive.text()
|
148
|
+
self.contact["SentNr"] = self.other_1.text().upper()
|
149
|
+
self.contact["NR"] = self.other_2.text().upper()
|
150
|
+
|
151
|
+
self.contact["IsMultiplier1"] = 0
|
152
|
+
self.contact["IsMultiplier2"] = 0
|
153
|
+
|
154
|
+
if (
|
155
|
+
self.contact.get("CountryPrefix", "") == "F"
|
156
|
+
and self.contact.get("NR", "").isalpha()
|
157
|
+
):
|
158
|
+
canton = self.contact.get("NR", "").upper()
|
159
|
+
band = self.contact.get("Band", "")
|
160
|
+
query = (
|
161
|
+
f"select count(*) as canton_count from dxlog where "
|
162
|
+
f"NR = '{canton}' "
|
163
|
+
f"and Band = '{band}' "
|
164
|
+
f"and ContestNR = {self.pref.get('contest', '1')};"
|
165
|
+
)
|
166
|
+
result = self.database.exec_sql(query)
|
167
|
+
count = int(result.get("canton_count", 0))
|
168
|
+
if count == 0:
|
169
|
+
self.contact["IsMultiplier1"] = 1
|
170
|
+
|
171
|
+
if self.contact.get("CountryPrefix", ""):
|
172
|
+
dxcc = self.contact.get("CountryPrefix", "")
|
173
|
+
band = self.contact.get("Band", "")
|
174
|
+
query = (
|
175
|
+
f"select count(*) as dxcc_count from dxlog where "
|
176
|
+
f"CountryPrefix = '{dxcc}' "
|
177
|
+
f"and Band = '{band}' "
|
178
|
+
f"and ContestNR = {self.pref.get('contest', '1')};"
|
179
|
+
)
|
180
|
+
result = self.database.exec_sql(query)
|
181
|
+
if not result.get("dxcc_count", ""):
|
182
|
+
self.contact["IsMultiplier2"] = 1
|
183
|
+
|
184
|
+
|
185
|
+
def predupe(self):
|
186
|
+
"""called after callsign entered"""
|
187
|
+
|
188
|
+
|
189
|
+
def prefill(self):
|
190
|
+
"""Fill SentNR"""
|
191
|
+
field = self.field3.findChild(QtWidgets.QLineEdit)
|
192
|
+
sent_sxchange_setting = self.contest_settings.get("SentExchange", "")
|
193
|
+
if sent_sxchange_setting.strip() == "#":
|
194
|
+
result = self.database.get_serial()
|
195
|
+
serial_nr = str(result.get("serial_nr", "1")).zfill(3)
|
196
|
+
if serial_nr == "None":
|
197
|
+
serial_nr = "001"
|
198
|
+
if len(field.text()) == 0:
|
199
|
+
field.setText(serial_nr)
|
200
|
+
else:
|
201
|
+
field.setText(sent_sxchange_setting)
|
202
|
+
|
203
|
+
|
204
|
+
def points(self):
|
205
|
+
"""
|
206
|
+
Scoring:
|
207
|
+
French: 6 points per QSO with French station same continent
|
208
|
+
French: 15 points per QSO with French station on different continent
|
209
|
+
French: 1 point per QSO with non-French station same continent
|
210
|
+
French: 2 points per QSO with non-French station on different continent
|
211
|
+
non-French: 1 point per QSO with French station same continent
|
212
|
+
non-French: 3 points per QSO with French station on different continent
|
213
|
+
|
214
|
+
self.contact["CountryPrefix"]
|
215
|
+
self.contact["Continent"]
|
216
|
+
"""
|
217
|
+
|
218
|
+
# Just incase the cty lookup fails
|
219
|
+
my_country = None
|
220
|
+
my_continent = None
|
221
|
+
their_continent = None
|
222
|
+
their_country = None
|
223
|
+
|
224
|
+
result = self.cty_lookup(self.station.get("Call", ""))
|
225
|
+
if result:
|
226
|
+
for item in result.items():
|
227
|
+
my_country = item[1].get("entity", "")
|
228
|
+
my_continent = item[1].get("continent", "")
|
229
|
+
result = self.cty_lookup(self.contact.get("Call", ""))
|
230
|
+
if result:
|
231
|
+
for item in result.items():
|
232
|
+
their_country = item[1].get("entity", "")
|
233
|
+
their_continent = item[1].get("continent", "")
|
234
|
+
|
235
|
+
if my_country == "France":
|
236
|
+
if their_country == "France":
|
237
|
+
if my_continent == their_continent:
|
238
|
+
return 6
|
239
|
+
else:
|
240
|
+
return 15
|
241
|
+
else:
|
242
|
+
if my_continent == their_continent:
|
243
|
+
return 1
|
244
|
+
else:
|
245
|
+
return 2
|
246
|
+
else:
|
247
|
+
if their_country == "France":
|
248
|
+
if their_continent == my_continent:
|
249
|
+
return 1
|
250
|
+
else:
|
251
|
+
return 3
|
252
|
+
|
253
|
+
return 0
|
254
|
+
|
255
|
+
|
256
|
+
def show_mults(self):
|
257
|
+
"""Return display string for mults"""
|
258
|
+
return int(self.database.fetch_mult_count(1).get("count", 0)) + int(
|
259
|
+
self.database.fetch_mult_count(2).get("count", 0)
|
260
|
+
)
|
261
|
+
|
262
|
+
|
263
|
+
def show_qso(self):
|
264
|
+
"""Return qso count"""
|
265
|
+
result = self.database.fetch_qso_count()
|
266
|
+
if result:
|
267
|
+
return int(result.get("qsos", 0))
|
268
|
+
return 0
|
269
|
+
|
270
|
+
|
271
|
+
def calc_score(self):
|
272
|
+
"""Return calculated score"""
|
273
|
+
result = self.database.fetch_points()
|
274
|
+
if result is not None:
|
275
|
+
score = result.get("Points", "0")
|
276
|
+
if score is None:
|
277
|
+
score = "0"
|
278
|
+
contest_points = int(score)
|
279
|
+
mults = show_mults(self)
|
280
|
+
return contest_points * mults
|
281
|
+
return 0
|
282
|
+
|
283
|
+
|
284
|
+
def recalculate_mults(self):
|
285
|
+
"""Recalculates multipliers after change in logged qso."""
|
286
|
+
|
287
|
+
all_contacts = self.database.fetch_all_contacts_asc()
|
288
|
+
for contact in all_contacts:
|
289
|
+
|
290
|
+
contact["IsMultiplier1"] = 0
|
291
|
+
contact["IsMultiplier2"] = 0
|
292
|
+
|
293
|
+
time_stamp = contact.get("TS", "")
|
294
|
+
canton = contact.get("NR", "")
|
295
|
+
dxcc = contact.get("CountryPrefix", "")
|
296
|
+
band = contact.get("Band", "")
|
297
|
+
if dxcc == "HB" and canton.isalpha():
|
298
|
+
query = (
|
299
|
+
f"select count(*) as canton_count from dxlog where TS < '{time_stamp}' "
|
300
|
+
f"and NR = '{canton.upper()}' "
|
301
|
+
f"and Band = '{band}' "
|
302
|
+
f"and ContestNR = {self.pref.get('contest', '1')};"
|
303
|
+
)
|
304
|
+
result = self.database.exec_sql(query)
|
305
|
+
count = int(result.get("canton_count", 0))
|
306
|
+
if count == 0:
|
307
|
+
contact["IsMultiplier1"] = 1
|
308
|
+
|
309
|
+
if dxcc:
|
310
|
+
query = (
|
311
|
+
f"select count(*) as dxcc_count from dxlog where TS < '{time_stamp}' "
|
312
|
+
f"and CountryPrefix = '{dxcc}' "
|
313
|
+
f"and Band = '{band}' "
|
314
|
+
f"and ContestNR = {self.pref.get('contest', '1')};"
|
315
|
+
)
|
316
|
+
result = self.database.exec_sql(query)
|
317
|
+
if not result.get("dxcc_count", ""):
|
318
|
+
contact["IsMultiplier2"] = 1
|
319
|
+
|
320
|
+
self.database.change_contact(contact)
|
321
|
+
cmd = {}
|
322
|
+
cmd["cmd"] = "UPDATELOG"
|
323
|
+
cmd["station"] = platform.node()
|
324
|
+
self.multicast_interface.send_as_json(cmd)
|
325
|
+
|
326
|
+
|
327
|
+
def adif(self):
|
328
|
+
"""Call the generate ADIF function"""
|
329
|
+
gen_adif(self, cabrillo_name, "HELVETIA")
|
330
|
+
|
331
|
+
|
332
|
+
def cabrillo(self):
|
333
|
+
"""Generates Cabrillo file. Maybe."""
|
334
|
+
# https://www.cqwpx.com/cabrillo.htm
|
335
|
+
logger.debug("******Cabrillo*****")
|
336
|
+
logger.debug("Station: %s", f"{self.station}")
|
337
|
+
logger.debug("Contest: %s", f"{self.contest_settings}")
|
338
|
+
now = datetime.datetime.now()
|
339
|
+
date_time = now.strftime("%Y-%m-%d_%H-%M-%S")
|
340
|
+
filename = (
|
341
|
+
str(Path.home())
|
342
|
+
+ "/"
|
343
|
+
+ f"{self.station.get('Call', '').upper()}_{cabrillo_name}_{date_time}.log"
|
344
|
+
)
|
345
|
+
logger.debug("%s", filename)
|
346
|
+
log = self.database.fetch_all_contacts_asc()
|
347
|
+
try:
|
348
|
+
with open(filename, "w", encoding="ascii") as file_descriptor:
|
349
|
+
print("START-OF-LOG: 3.0", end="\r\n", file=file_descriptor)
|
350
|
+
print(
|
351
|
+
f"CREATED-BY: Not1MM v{__version__}",
|
352
|
+
end="\r\n",
|
353
|
+
file=file_descriptor,
|
354
|
+
)
|
355
|
+
print(
|
356
|
+
f"CONTEST: {cabrillo_name}",
|
357
|
+
end="\r\n",
|
358
|
+
file=file_descriptor,
|
359
|
+
)
|
360
|
+
if self.station.get("Club", ""):
|
361
|
+
print(
|
362
|
+
f"CLUB: {self.station.get('Club', '').upper()}",
|
363
|
+
end="\r\n",
|
364
|
+
file=file_descriptor,
|
365
|
+
)
|
366
|
+
print(
|
367
|
+
f"CALLSIGN: {self.station.get('Call','')}",
|
368
|
+
end="\r\n",
|
369
|
+
file=file_descriptor,
|
370
|
+
)
|
371
|
+
print(
|
372
|
+
f"LOCATION: {self.station.get('ARRLSection', '')}",
|
373
|
+
end="\r\n",
|
374
|
+
file=file_descriptor,
|
375
|
+
)
|
376
|
+
# print(
|
377
|
+
# f"ARRL-SECTION: {self.pref.get('section', '')}",
|
378
|
+
# end="\r\n",
|
379
|
+
# file=file_descriptor,
|
380
|
+
# )
|
381
|
+
print(
|
382
|
+
f"CATEGORY-OPERATOR: {self.contest_settings.get('OperatorCategory','')}",
|
383
|
+
end="\r\n",
|
384
|
+
file=file_descriptor,
|
385
|
+
)
|
386
|
+
print(
|
387
|
+
f"CATEGORY-ASSISTED: {self.contest_settings.get('AssistedCategory','')}",
|
388
|
+
end="\r\n",
|
389
|
+
file=file_descriptor,
|
390
|
+
)
|
391
|
+
print(
|
392
|
+
f"CATEGORY-BAND: {self.contest_settings.get('BandCategory','')}",
|
393
|
+
end="\r\n",
|
394
|
+
file=file_descriptor,
|
395
|
+
)
|
396
|
+
print(
|
397
|
+
f"CATEGORY-MODE: {self.contest_settings.get('ModeCategory','')}",
|
398
|
+
end="\r\n",
|
399
|
+
file=file_descriptor,
|
400
|
+
)
|
401
|
+
print(
|
402
|
+
f"CATEGORY-TRANSMITTER: {self.contest_settings.get('TransmitterCategory','')}",
|
403
|
+
end="\r\n",
|
404
|
+
file=file_descriptor,
|
405
|
+
)
|
406
|
+
if self.contest_settings.get("OverlayCategory", "") != "N/A":
|
407
|
+
print(
|
408
|
+
f"CATEGORY-OVERLAY: {self.contest_settings.get('OverlayCategory','')}",
|
409
|
+
end="\r\n",
|
410
|
+
file=file_descriptor,
|
411
|
+
)
|
412
|
+
print(
|
413
|
+
f"GRID-LOCATOR: {self.station.get('GridSquare','')}",
|
414
|
+
end="\r\n",
|
415
|
+
file=file_descriptor,
|
416
|
+
)
|
417
|
+
# print(
|
418
|
+
# f"CATEGORY: {None}",
|
419
|
+
# end="\r\n",
|
420
|
+
# file=file_descriptor,
|
421
|
+
# )
|
422
|
+
print(
|
423
|
+
f"CATEGORY-POWER: {self.contest_settings.get('PowerCategory','')}",
|
424
|
+
end="\r\n",
|
425
|
+
file=file_descriptor,
|
426
|
+
)
|
427
|
+
|
428
|
+
print(
|
429
|
+
f"CLAIMED-SCORE: {calc_score(self)}",
|
430
|
+
end="\r\n",
|
431
|
+
file=file_descriptor,
|
432
|
+
)
|
433
|
+
ops = f"@{self.station.get('Call','')}"
|
434
|
+
list_of_ops = self.database.get_ops()
|
435
|
+
for op in list_of_ops:
|
436
|
+
ops += f", {op.get('Operator', '')}"
|
437
|
+
print(
|
438
|
+
f"OPERATORS: {ops}",
|
439
|
+
end="\r\n",
|
440
|
+
file=file_descriptor,
|
441
|
+
)
|
442
|
+
print(
|
443
|
+
f"NAME: {self.station.get('Name', '')}",
|
444
|
+
end="\r\n",
|
445
|
+
file=file_descriptor,
|
446
|
+
)
|
447
|
+
print(
|
448
|
+
f"ADDRESS: {self.station.get('Street1', '')}",
|
449
|
+
end="\r\n",
|
450
|
+
file=file_descriptor,
|
451
|
+
)
|
452
|
+
print(
|
453
|
+
f"ADDRESS-CITY: {self.station.get('City', '')}",
|
454
|
+
end="\r\n",
|
455
|
+
file=file_descriptor,
|
456
|
+
)
|
457
|
+
print(
|
458
|
+
f"ADDRESS-STATE-PROVINCE: {self.station.get('State', '')}",
|
459
|
+
end="\r\n",
|
460
|
+
file=file_descriptor,
|
461
|
+
)
|
462
|
+
print(
|
463
|
+
f"ADDRESS-POSTALCODE: {self.station.get('Zip', '')}",
|
464
|
+
end="\r\n",
|
465
|
+
file=file_descriptor,
|
466
|
+
)
|
467
|
+
print(
|
468
|
+
f"ADDRESS-COUNTRY: {self.station.get('Country', '')}",
|
469
|
+
end="\r\n",
|
470
|
+
file=file_descriptor,
|
471
|
+
)
|
472
|
+
print(
|
473
|
+
f"EMAIL: {self.station.get('Email', '')}",
|
474
|
+
end="\r\n",
|
475
|
+
file=file_descriptor,
|
476
|
+
)
|
477
|
+
for contact in log:
|
478
|
+
the_date_and_time = contact.get("TS", "")
|
479
|
+
themode = contact.get("Mode", "")
|
480
|
+
if themode == "LSB" or themode == "USB":
|
481
|
+
themode = "PH"
|
482
|
+
frequency = str(int(contact.get("Freq", "0"))).rjust(5)
|
483
|
+
|
484
|
+
loggeddate = the_date_and_time[:10]
|
485
|
+
loggedtime = the_date_and_time[11:13] + the_date_and_time[14:16]
|
486
|
+
print(
|
487
|
+
f"QSO: {frequency} {themode} {loggeddate} {loggedtime} "
|
488
|
+
f"{contact.get('StationPrefix', '').ljust(13)} "
|
489
|
+
f"{str(contact.get('SNT', '')).ljust(3)} "
|
490
|
+
f"{str(contact.get('SentNr', '')).ljust(6)} "
|
491
|
+
f"{contact.get('Call', '').ljust(13)} "
|
492
|
+
f"{str(contact.get('RCV', '')).ljust(3)} "
|
493
|
+
f"{str(contact.get('NR', '')).ljust(6)}",
|
494
|
+
end="\r\n",
|
495
|
+
file=file_descriptor,
|
496
|
+
)
|
497
|
+
print("END-OF-LOG:", end="\r\n", file=file_descriptor)
|
498
|
+
self.show_message_box(f"Cabrillo saved to: {filename}")
|
499
|
+
except IOError as exception:
|
500
|
+
logger.critical("cabrillo: IO error: %s, writing to %s", exception, filename)
|
501
|
+
self.show_message_box(f"Error saving Cabrillo: {exception} {filename}")
|
502
|
+
return
|
not1mm/test.py
CHANGED
@@ -1,13 +1,6 @@
|
|
1
|
-
|
2
|
-
"""
|
3
|
-
NOT1MM Logger
|
4
|
-
Purpose: test alternative sound playing interface
|
5
|
-
"""
|
6
|
-
# pylint: disable=unused-import, c-extension-no-member, no-member, invalid-name, too-many-lines, no-name-in-module
|
7
|
-
# pylint: disable=logging-fstring-interpolation, logging-not-lazy, line-too-long, bare-except
|
1
|
+
import xmlrpc.client
|
8
2
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
print(f"{parse_udc(filename)}")
|
3
|
+
target = "http://127.0.0.1:8421"
|
4
|
+
server = xmlrpc.client.ServerProxy(target)
|
5
|
+
adif = "<QSO_DATE:8>20150721<QSO_DATE_OFF:8>20150721<TIME_ON:4>1333<TIME_OFF:6>133436<CALL:5>N3FJP<FREQ:8>3.081500<MODE:0><RST_SENT:0><RST_RCVD:0><TX_PWR:0><NAME:5>Glenn<QTH:7>Bel Air<STATE:2>MD<VE_PROV:0><COUNTRY:13>United States<GRIDSQUARE:6>FM19tm<STX:0><SRX:0><SRX_STRING:0><STX_STRING:0><NOTES:0><IOTA:0><DXCC:0><QSL_VIA:0><QSLRDATE:0><QSLSDATE:0><eor>"
|
6
|
+
response = server.log.add_record(adif)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: not1mm
|
3
|
-
Version: 24.
|
3
|
+
Version: 24.9.5
|
4
4
|
Summary: NOT1MM Logger
|
5
5
|
Author-email: Michael Bridak <michael.bridak@gmail.com>
|
6
6
|
Project-URL: Homepage, https://github.com/mbridak/not1mm
|
@@ -100,6 +100,7 @@ Requires-Dist: Levenshtein
|
|
100
100
|
- [Cluster](#cluster)
|
101
101
|
- [N1MM Packets](#n1mm-packets)
|
102
102
|
- [Bands](#bands)
|
103
|
+
- [Logging WSJT-X FT8/FT4 contacts](#logging-wsjt-x-ft8ft4-contacts)
|
103
104
|
- [Sending CW](#sending-cw)
|
104
105
|
- [Editing macro keys](#editing-macro-keys)
|
105
106
|
- [Macro substitutions](#macro-substitutions)
|
@@ -225,11 +226,8 @@ generated, 'cause I'm lazy, list of those who've submitted PR's.
|
|
225
226
|
|
226
227
|
## Recent Changes
|
227
228
|
|
228
|
-
- [24-
|
229
|
-
- [24-
|
230
|
-
- [24-8-20] Added K1USN Slow Speed Test
|
231
|
-
- [24-8-17-1] Did an oops. Fixed the oops.
|
232
|
-
- [24-8-17] Removed some cruft. Made dockable widgets not floatable since Wayland breaks this.
|
229
|
+
- [24-9-5] Added FlDigi support for Field Day.
|
230
|
+
- [24-9-3] Added WSJT-X FT8 mode contacts to ARRL Field Day.
|
233
231
|
|
234
232
|
See [CHANGELOG.md](CHANGELOG.md) for prior changes.
|
235
233
|
|
@@ -250,7 +248,7 @@ not1mm requires:
|
|
250
248
|
- PyQt6
|
251
249
|
- libportaudio2
|
252
250
|
- libxcb-cursor0 (maybe... Depends on the distro)
|
253
|
-
|
251
|
+
|
254
252
|
You should install these through your distribution's package manager before continuing.
|
255
253
|
|
256
254
|
### Common installation recipes for Ubuntu and Fedora
|
@@ -337,9 +335,7 @@ pamac build not1mm-git
|
|
337
335
|
#### Mint
|
338
336
|
|
339
337
|
```bash
|
340
|
-
sudo apt install python3-pip
|
341
|
-
sudo apt install pipx
|
342
|
-
sudo apt install libxcb-cursor0
|
338
|
+
sudo apt install python3-pip pipx libxcb-cursor0
|
343
339
|
pipx install not1mm
|
344
340
|
pipx ensurepath
|
345
341
|
```
|
@@ -608,6 +604,13 @@ appear. Those without will not.
|
|
608
604
|
|
609
605
|

|
610
606
|
|
607
|
+
## Logging WSJT-X FT8/FT4 contacts
|
608
|
+
|
609
|
+
**Currently only working for ARRL Field Day.**
|
610
|
+
|
611
|
+
not1mm listens for WSJT-X UDP traffic on the default localhost:2237. No setup is
|
612
|
+
needed to be done on not1mm's side.
|
613
|
+
|
611
614
|
## Sending CW
|
612
615
|
|
613
616
|
Other than sending CW by hand, you can also send predefined CW text messages by
|
@@ -1,11 +1,11 @@
|
|
1
1
|
not1mm/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
2
|
-
not1mm/__main__.py,sha256=
|
2
|
+
not1mm/__main__.py,sha256=C9DOp1FLdRj1Oako2gW9Y6rlfHniSp379RIhIKiYgEw,124001
|
3
3
|
not1mm/bandmap.py,sha256=1b5tXCfGTnpqqn6hPNt7zRA8SmuwSXzSwNHZXhCRt70,31434
|
4
4
|
not1mm/checkwindow.py,sha256=aI-nr8OF90IWV7R_XRdmitvBJ9M85evCs72HoU3Jnvc,10374
|
5
5
|
not1mm/fsutils.py,sha256=ukHKxKTeNKxKwqRaJjtzRShL4X5Xl0jRBbADyy3Ifp8,1701
|
6
6
|
not1mm/logwindow.py,sha256=pwhiwolmGnW01LF4sjlu3ywLsgfxL6KuGuKuYKYmgeY,44403
|
7
7
|
not1mm/radio.py,sha256=eiB04LPMPBeMrBRI021Z7VXth66EOYb0Ujh11T9877c,3362
|
8
|
-
not1mm/test.py,sha256=
|
8
|
+
not1mm/test.py,sha256=QE9lemU13glwB2yuBAuXXQHzADTW1vSX90HHu0F-Akg,496
|
9
9
|
not1mm/vfo.py,sha256=IvmUQYMIPzLJw_BHQGis4J_IEW-vlBtdfxZLXPh7OzI,12335
|
10
10
|
not1mm/voice_keying.py,sha256=sA3gw5_k7kShTg2qhG7HkKDM5M6KheJVRkAc_C7mxDk,3006
|
11
11
|
not1mm/data/JetBrainsMono-Regular.ttf,sha256=UOHctAKY_PzCGh7zy-6f6egnCcSK0wzmF0csBqO9lDY,203952
|
@@ -94,12 +94,13 @@ not1mm/lib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
94
94
|
not1mm/lib/about.py,sha256=sWycfGcruN3SaEe4JmaJ61K6D8Itq0WxpUYT-lEcmYM,416
|
95
95
|
not1mm/lib/cat_interface.py,sha256=aazvNTSeZAROq3KL8gPx-I95iVez2IiIOSk22qeqVCQ,19502
|
96
96
|
not1mm/lib/cwinterface.py,sha256=Q8p3pScOHczZ8ptICfH1Yu6rCEwQJLgrNwYMN76B2i8,3389
|
97
|
-
not1mm/lib/database.py,sha256=
|
97
|
+
not1mm/lib/database.py,sha256=0dx6SX40OiPFent2ePAGsFj_XmOVMNBHaqd0QtAiewY,43129
|
98
98
|
not1mm/lib/edit_contact.py,sha256=Ki9bGPpqyQQBB1cU8VIBDCal3lbXeQ6qxhzklmhE2_w,353
|
99
99
|
not1mm/lib/edit_macro.py,sha256=raKWBwsHInj5EUKmvyLQ6gqc3ZFDlstsD3xqoM4PC8E,517
|
100
100
|
not1mm/lib/edit_opon.py,sha256=j3qJ1aBsQoIOnQ9yiBl3lyeISvKTP0I_rtBYBPAfgeI,359
|
101
101
|
not1mm/lib/edit_station.py,sha256=doL21Hs6jzIE43ohAopdFt_iqnRJZHFcqzcnCS0-iio,1965
|
102
|
-
not1mm/lib/
|
102
|
+
not1mm/lib/fldigi_watcher.py,sha256=k_fAaJLdt4mwyKKnpiZrV-rSFtRbi2C8y1Dg3eyILIU,883
|
103
|
+
not1mm/lib/ft8_watcher.py,sha256=M9R5OUs3i99u3IfKnv1KYIMfljPViSbk-VeyjsVQLp4,4100
|
103
104
|
not1mm/lib/ham_utility.py,sha256=uRErxCxZr8dfxzekPyett0e_BABDVOCvSUUTzXq6ctE,11790
|
104
105
|
not1mm/lib/lookup.py,sha256=F2fl5QkMxaGSxl1XMWnLUub3T9Mt7LhCX4acOlAsks4,13952
|
105
106
|
not1mm/lib/multicast.py,sha256=bnFUNHyy82GmIb3_88EPBVVssj7-HzkJPaH671cK8Qw,3249
|
@@ -110,7 +111,7 @@ not1mm/lib/plugin_common.py,sha256=wuG7B0OJx9zYc5Gew3fdt_lNyan8Ul9KNlPQ7PDKGsU,9
|
|
110
111
|
not1mm/lib/select_contest.py,sha256=WsptLuwkouIHeocJL3oZ6-eUfEnhpwdc-x7eMZ_TIVM,359
|
111
112
|
not1mm/lib/settings.py,sha256=MWiKXbasaFbzeHTjfzTaTqbCBrIijudP_-0a5jNmUAA,9265
|
112
113
|
not1mm/lib/super_check_partial.py,sha256=p5l3u2ZOCBtlWgbvskC50FpuoaIpR07tfC6zTdRWbh4,2334
|
113
|
-
not1mm/lib/version.py,sha256=
|
114
|
+
not1mm/lib/version.py,sha256=F98YdDdv2B3tvIDuoqpiagUBxJ2s48EdRr4FvAbwOnc,47
|
114
115
|
not1mm/lib/versiontest.py,sha256=8vDNptuBBunn-1IGkjNaquehqBYUJyjrPSF8Igmd4_Y,1286
|
115
116
|
not1mm/plugins/10_10_fall_cw.py,sha256=IttjX1yy4nDdACGsiYlPteFG8eVseX_WtoFio6bqHE8,10953
|
116
117
|
not1mm/plugins/10_10_spring_cw.py,sha256=ThCptdM3dX4ywhoy2JRcOEyHSqcJolFaT7O_PYzM1Mg,10958
|
@@ -120,7 +121,7 @@ not1mm/plugins/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
120
121
|
not1mm/plugins/arrl_10m.py,sha256=EyNRb3Sm0Qb-GVm0p05EnadlHisVh7y-sKTBP2ddMLo,13768
|
121
122
|
not1mm/plugins/arrl_dx_cw.py,sha256=LVnYDNFEUiIpQ1TlhOCcRK7JNwH5XPO5WzUoApSUMTY,13802
|
122
123
|
not1mm/plugins/arrl_dx_ssb.py,sha256=fUFzuNbCAfA5sQSYm8ISV3P9Z_2xnuKeOdO6E66zn1k,13805
|
123
|
-
not1mm/plugins/arrl_field_day.py,sha256=
|
124
|
+
not1mm/plugins/arrl_field_day.py,sha256=pivtEK5j9sLpta12_wuUQYvs7MuiP3JfhlO-AOIDJug,12707
|
124
125
|
not1mm/plugins/arrl_rtty_ru.py,sha256=hKUS4isjdXo3EYxQrsqsDupPp2chW8fpoWj0T1pTgJ4,7994
|
125
126
|
not1mm/plugins/arrl_ss_cw.py,sha256=4yN68xZMuJRaSUfdiY4hafC07A3lifl5q6DEUZ-oYPQ,13080
|
126
127
|
not1mm/plugins/arrl_ss_phone.py,sha256=Yzc5sNjrY8TlnozbYF6k8hbEamyDuUAD_3-BNqHgXqY,13068
|
@@ -128,15 +129,15 @@ not1mm/plugins/arrl_vhf_jan.py,sha256=jkX9v2HHsjoawtTKiy8X0Td_HtfGQVT3MapJwpmM1q
|
|
128
129
|
not1mm/plugins/arrl_vhf_jun.py,sha256=_9h6joxVCRCoFuNnyTOlnonafvrMv7H0eBERhYCoGug,11591
|
129
130
|
not1mm/plugins/arrl_vhf_sep.py,sha256=XbS1OSAfGaYXFaTAP2HRyVvVeRD_Z6cefPc1_mOLJKw,11591
|
130
131
|
not1mm/plugins/canada_day.py,sha256=OVpcCl1Chse_zLHf6PayTrgawWM4W-pmrTw40Al-o9s,11998
|
131
|
-
not1mm/plugins/cq_160_cw.py,sha256=
|
132
|
-
not1mm/plugins/cq_160_ssb.py,sha256=
|
132
|
+
not1mm/plugins/cq_160_cw.py,sha256=5s6rIZdJEnmWe1SI06BEyz7p5vP0N2n9mI4l_mZ0icw,14139
|
133
|
+
not1mm/plugins/cq_160_ssb.py,sha256=zIwSMAjHSt6W2edrDzVbyTf860JowHoFkU9BKO8Enag,14182
|
133
134
|
not1mm/plugins/cq_wpx_cw.py,sha256=9aNzAR-KhznIwUlxUFjAi_hbiw_6RrCMwUBk9I2f6Hs,14037
|
134
135
|
not1mm/plugins/cq_wpx_ssb.py,sha256=-hGRovqHR9rfOUnG4LPOoABTb4heH8VAX6rYdJbCqsw,12687
|
135
136
|
not1mm/plugins/cq_ww_cw.py,sha256=ltXFnSXabCOuW70s-WOydgghZTNpztX8TKLpVIV50B4,11194
|
136
137
|
not1mm/plugins/cq_ww_ssb.py,sha256=kt-EQofmCbynX1iXFm9ehffi_TMW25ke8Qi9MiR69ZQ,11199
|
137
138
|
not1mm/plugins/cwt.py,sha256=4xdXN6ZJM5k-6gn0hJzNheWfFlGiqquC2p0ZMEe516M,12818
|
138
139
|
not1mm/plugins/general_logging.py,sha256=t02xtJs601qRICGdrvLs3G9y4GCG9H4AgQNkgA18CYs,3467
|
139
|
-
not1mm/plugins/helvetia.py,sha256=
|
140
|
+
not1mm/plugins/helvetia.py,sha256=6aOO4uiLzFFgHA-A3xz6IRdCJpqPOAm0egKxP5Y_Ie0,15432
|
140
141
|
not1mm/plugins/iaru_hf.py,sha256=-ROUo2gBkw3xB89t8bd-4f7_1hROw2VXZXVHLFdB62s,11541
|
141
142
|
not1mm/plugins/icwc_mst.py,sha256=BaUP2kzrT2D27un_WLGT4HCTTi1e7CNYC4NHcC_9r74,11842
|
142
143
|
not1mm/plugins/jidx_cw.py,sha256=9oV4hDxMiGXa9wuYUNYOCsr-mz8LYB-4WMHBN8u2dFk,12153
|
@@ -145,11 +146,12 @@ not1mm/plugins/k1usn_sst.py,sha256=2Nu7SRiQeUG3mL9CLKReRLh8vKsNbWcizMgv9MTLkrg,1
|
|
145
146
|
not1mm/plugins/naqp_cw.py,sha256=c0MuKqfkIxiYFvv2z7vqrBz3m9FSnSYkPK3f-DdkTIA,12632
|
146
147
|
not1mm/plugins/naqp_ssb.py,sha256=VLWVrSzI0UP1AhSXYn61eZ7or1rz6a_pS_xCKfgS4Jw,11595
|
147
148
|
not1mm/plugins/phone_weekly_test.py,sha256=fLpMe03WB9_KgRl6vMgQQt_aktFdqfNt2Sw81CTRAUs,12325
|
149
|
+
not1mm/plugins/ref_cw.py,sha256=aWjHHkqIKutjRUtzh09y5haFfnZK9poRQDWRQMDRxxU,16326
|
148
150
|
not1mm/plugins/stew_perry_topband.py,sha256=CKBQbYl4ETxhXJd2dma4fg_C5pag_s7Nf61SCztZtqE,10668
|
149
151
|
not1mm/plugins/winter_field_day.py,sha256=4rcfRtobwjHO6BNL3WOTHzBmyyeuX79BNGBG8PfjrI8,10238
|
150
|
-
not1mm-24.
|
151
|
-
not1mm-24.
|
152
|
-
not1mm-24.
|
153
|
-
not1mm-24.
|
154
|
-
not1mm-24.
|
155
|
-
not1mm-24.
|
152
|
+
not1mm-24.9.5.dist-info/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
153
|
+
not1mm-24.9.5.dist-info/METADATA,sha256=ARDMBkO0MbKxknvDqFFkeCqup5PWor3Ag0hZtqDWl_8,29781
|
154
|
+
not1mm-24.9.5.dist-info/WHEEL,sha256=cVxcB9AmuTcXqmwrtPhNK88dr7IR_b6qagTj0UvIEbY,91
|
155
|
+
not1mm-24.9.5.dist-info/entry_points.txt,sha256=pMcZk_0dxFgLkcUkF0Q874ojpwOmF3OL6EKw9LgvocM,47
|
156
|
+
not1mm-24.9.5.dist-info/top_level.txt,sha256=0YmTxEcDzQlzXub-lXASvoLpg_mt1c2thb5cVkDf5J4,7
|
157
|
+
not1mm-24.9.5.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|