not1mm 24.8.20__py3-none-any.whl → 24.9.3__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 +77 -39
- not1mm/data/new_contest.ui +6 -1
- not1mm/data/splash.png +0 -0
- not1mm/lib/database.py +20 -3
- not1mm/lib/ham_utility.py +26 -0
- not1mm/lib/version.py +1 -1
- not1mm/plugins/arrl_field_day.py +14 -3
- not1mm/plugins/cq_160_cw.py +2 -2
- not1mm/plugins/cq_160_ssb.py +2 -2
- not1mm/plugins/helvetia.py +497 -0
- not1mm/plugins/ref_cw.py +502 -0
- not1mm/test.py +10 -0
- {not1mm-24.8.20.dist-info → not1mm-24.9.3.dist-info}/METADATA +37 -7
- {not1mm-24.8.20.dist-info → not1mm-24.9.3.dist-info}/RECORD +18 -15
- {not1mm-24.8.20.dist-info → not1mm-24.9.3.dist-info}/WHEEL +1 -1
- not1mm/playsoundtest.py +0 -15
- {not1mm-24.8.20.dist-info → not1mm-24.9.3.dist-info}/LICENSE +0 -0
- {not1mm-24.8.20.dist-info → not1mm-24.9.3.dist-info}/entry_points.txt +0 -0
- {not1mm-24.8.20.dist-info → not1mm-24.9.3.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
|
|
@@ -35,9 +36,9 @@ except OSError as exception:
|
|
35
36
|
print("portaudio is not installed")
|
36
37
|
sd = None
|
37
38
|
from PyQt6 import QtCore, QtGui, QtWidgets, uic
|
38
|
-
from PyQt6.QtCore import QDir, Qt, QThread, QSettings
|
39
|
-
from PyQt6.QtGui import QFontDatabase, QColorConstants, QPalette, QColor
|
40
|
-
from PyQt6.QtWidgets import QFileDialog
|
39
|
+
from PyQt6.QtCore import QDir, Qt, QThread, QSettings, QCoreApplication
|
40
|
+
from PyQt6.QtGui import QFontDatabase, QColorConstants, QPalette, QColor, QPixmap
|
41
|
+
from PyQt6.QtWidgets import QFileDialog, QSplashScreen
|
41
42
|
|
42
43
|
from not1mm.lib.about import About
|
43
44
|
from not1mm.lib.cwinterface import CW
|
@@ -186,8 +187,7 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
186
187
|
)
|
187
188
|
self.setCorner(Qt.Corner.TopLeftCorner, Qt.DockWidgetArea.LeftDockWidgetArea)
|
188
189
|
self.setCorner(Qt.Corner.BottomLeftCorner, Qt.DockWidgetArea.LeftDockWidgetArea)
|
189
|
-
|
190
|
-
uic.loadUi(data_path, self)
|
190
|
+
uic.loadUi(fsutils.APP_DATA_PATH / "main.ui", self)
|
191
191
|
self.cw_entry.hide()
|
192
192
|
self.leftdot.hide()
|
193
193
|
self.rightdot.hide()
|
@@ -459,8 +459,15 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
459
459
|
self.setWindowIcon(
|
460
460
|
QtGui.QIcon(str(fsutils.APP_DATA_PATH / "k6gte.not1mm-32.png"))
|
461
461
|
)
|
462
|
-
|
463
|
-
|
462
|
+
|
463
|
+
try:
|
464
|
+
with open(
|
465
|
+
fsutils.APP_DATA_PATH / "cty.json", "rt", encoding="utf-8"
|
466
|
+
) as c_file:
|
467
|
+
self.ctyfile = loads(c_file.read())
|
468
|
+
except (IOError, JSONDecodeError, TypeError):
|
469
|
+
logging.CRITICAL("There was an error parsing the BigCity file.")
|
470
|
+
|
464
471
|
self.readpreferences()
|
465
472
|
|
466
473
|
self.voice_process = Voice()
|
@@ -491,9 +498,7 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
491
498
|
self.make_op_dir()
|
492
499
|
|
493
500
|
self.clearinputs()
|
494
|
-
|
495
|
-
if self.pref.get("contest"):
|
496
|
-
self.load_contest()
|
501
|
+
self.load_contest()
|
497
502
|
self.read_cw_macros()
|
498
503
|
|
499
504
|
# Featureset for wayland
|
@@ -1287,10 +1292,15 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
1287
1292
|
if updated:
|
1288
1293
|
cty.dump(fsutils.APP_DATA_PATH / "cty.json")
|
1289
1294
|
self.show_message_box("cty file updated.")
|
1290
|
-
|
1291
|
-
|
1292
|
-
|
1293
|
-
|
1295
|
+
try:
|
1296
|
+
with open(
|
1297
|
+
fsutils.APP_DATA_PATH / "cty.json", "rt", encoding="utf-8"
|
1298
|
+
) as ctyfile:
|
1299
|
+
self.ctyfile = loads(ctyfile.read())
|
1300
|
+
except (IOError, JSONDecodeError, TypeError) as err:
|
1301
|
+
logging.CRITICAL(
|
1302
|
+
f"There was an error {err} parsing the BigCity file."
|
1303
|
+
)
|
1294
1304
|
else:
|
1295
1305
|
self.show_message_box("An Error occured updating file.")
|
1296
1306
|
else:
|
@@ -1898,11 +1908,14 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
1898
1908
|
" "
|
1899
1909
|
)[:19]
|
1900
1910
|
self.contact["Call"] = self.callsign.text()
|
1901
|
-
self.contact
|
1902
|
-
|
1903
|
-
|
1904
|
-
|
1905
|
-
|
1911
|
+
if self.contact.get("Mode") not in ("FT8", "FT4"):
|
1912
|
+
self.contact["Freq"] = round(
|
1913
|
+
float(self.radio_state.get("vfoa", 0.0)) / 1000, 2
|
1914
|
+
)
|
1915
|
+
self.contact["QSXFreq"] = round(
|
1916
|
+
float(self.radio_state.get("vfoa", 0.0)) / 1000, 2
|
1917
|
+
)
|
1918
|
+
self.contact["Mode"] = self.radio_state.get("mode", "")
|
1906
1919
|
self.contact["ContestName"] = self.contest.cabrillo_name
|
1907
1920
|
self.contact["ContestNR"] = self.pref.get("contest", "0")
|
1908
1921
|
self.contact["StationPrefix"] = self.station.get("Call", "")
|
@@ -2093,7 +2106,7 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
2093
2106
|
|
2094
2107
|
def save_settings(self) -> None:
|
2095
2108
|
"""
|
2096
|
-
Save settings to database.
|
2109
|
+
Save Station settings to database.
|
2097
2110
|
|
2098
2111
|
Parameters
|
2099
2112
|
----------
|
@@ -2322,7 +2335,7 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
2322
2335
|
with open(fsutils.CONFIG_FILE, "wt", encoding="utf-8") as file_descriptor:
|
2323
2336
|
file_descriptor.write(dumps(self.pref, indent=4))
|
2324
2337
|
# logger.info("writing: %s", self.pref)
|
2325
|
-
except IOError as exception:
|
2338
|
+
except (IOError, TypeError, ValueError) as exception:
|
2326
2339
|
logger.critical("writepreferences: %s", exception)
|
2327
2340
|
|
2328
2341
|
def readpreferences(self) -> None:
|
@@ -2344,7 +2357,12 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
2344
2357
|
with open(
|
2345
2358
|
fsutils.CONFIG_FILE, "rt", encoding="utf-8"
|
2346
2359
|
) as file_descriptor:
|
2347
|
-
|
2360
|
+
try:
|
2361
|
+
self.pref = loads(file_descriptor.read())
|
2362
|
+
except (JSONDecodeError, TypeError):
|
2363
|
+
logging.CRITICAL(
|
2364
|
+
"There was an error parsing the preference file."
|
2365
|
+
)
|
2348
2366
|
logger.info("%s", self.pref)
|
2349
2367
|
else:
|
2350
2368
|
logger.info("No preference file. Writing preference.")
|
@@ -2354,7 +2372,7 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
2354
2372
|
self.pref = self.pref_ref.copy()
|
2355
2373
|
file_descriptor.write(dumps(self.pref, indent=4))
|
2356
2374
|
logger.info("%s", self.pref)
|
2357
|
-
except IOError as exception:
|
2375
|
+
except (IOError, TypeError, ValueError) as exception:
|
2358
2376
|
logger.critical("Error: %s", exception)
|
2359
2377
|
|
2360
2378
|
self.look_up = None
|
@@ -3197,10 +3215,14 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
3197
3215
|
else:
|
3198
3216
|
macro_file = "ssbmacros.txt"
|
3199
3217
|
if not (fsutils.USER_DATA_PATH / macro_file).exists():
|
3200
|
-
logger.debug("
|
3201
|
-
|
3202
|
-
|
3203
|
-
|
3218
|
+
logger.debug("copying default macro file.")
|
3219
|
+
try:
|
3220
|
+
copyfile(
|
3221
|
+
fsutils.APP_DATA_PATH / macro_file,
|
3222
|
+
fsutils.USER_DATA_PATH / macro_file,
|
3223
|
+
)
|
3224
|
+
except IOError as err:
|
3225
|
+
logger.critical(f"Error {err} copying macro file.")
|
3204
3226
|
try:
|
3205
3227
|
fsutils.openFileWithOS(fsutils.USER_DATA_PATH / macro_file)
|
3206
3228
|
except FileNotFoundError | PermissionError | OSError as err:
|
@@ -3221,22 +3243,26 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
3221
3243
|
macro_file = "ssbmacros.txt"
|
3222
3244
|
|
3223
3245
|
if not (fsutils.USER_DATA_PATH / macro_file).exists():
|
3224
|
-
logger.debug("
|
3225
|
-
|
3226
|
-
|
3227
|
-
|
3228
|
-
|
3229
|
-
|
3230
|
-
|
3231
|
-
|
3232
|
-
|
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.")
|
3254
|
+
try:
|
3255
|
+
with open(
|
3256
|
+
fsutils.USER_DATA_PATH / macro_file, "r", encoding="utf-8"
|
3257
|
+
) as file_descriptor:
|
3258
|
+
for line in file_descriptor:
|
3233
3259
|
mode, fkey, buttonname, cwtext = line.split("|")
|
3234
3260
|
if mode.strip().upper() == "R" and self.pref.get("run_state"):
|
3235
3261
|
self.fkeys[fkey.strip()] = (buttonname.strip(), cwtext.strip())
|
3236
3262
|
if mode.strip().upper() != "R" and not self.pref.get("run_state"):
|
3237
3263
|
self.fkeys[fkey.strip()] = (buttonname.strip(), cwtext.strip())
|
3238
|
-
|
3239
|
-
|
3264
|
+
except (IOError, ValueError) as err:
|
3265
|
+
logger.info("read_cw_macros: %s", err)
|
3240
3266
|
keys = self.fkeys.keys()
|
3241
3267
|
if "F1" in keys:
|
3242
3268
|
self.F1.setText(f"F1: {self.fkeys['F1'][0]}")
|
@@ -3398,12 +3424,24 @@ logging.basicConfig(
|
|
3398
3424
|
logging.getLogger("PyQt6.uic.uiparser").setLevel("INFO")
|
3399
3425
|
logging.getLogger("PyQt6.uic.properties").setLevel("INFO")
|
3400
3426
|
app = QtWidgets.QApplication(sys.argv)
|
3427
|
+
|
3428
|
+
pixmap = QPixmap(f"{os.fspath(fsutils.APP_DATA_PATH)}/splash.png")
|
3429
|
+
splash = QSplashScreen(pixmap)
|
3430
|
+
splash.show()
|
3431
|
+
app.processEvents()
|
3432
|
+
splash.showMessage(
|
3433
|
+
"Starting Up",
|
3434
|
+
alignment=Qt.AlignmentFlag.AlignBottom | Qt.AlignmentFlag.AlignCenter,
|
3435
|
+
color=QColor(255, 255, 0),
|
3436
|
+
)
|
3437
|
+
QCoreApplication.processEvents()
|
3438
|
+
|
3401
3439
|
families = load_fonts_from_dir(os.fspath(fsutils.APP_DATA_PATH))
|
3402
3440
|
logger.info(f"font families {families}")
|
3403
3441
|
window = MainWindow()
|
3404
3442
|
window.callsign.setFocus()
|
3405
3443
|
window.show()
|
3406
|
-
|
3444
|
+
splash.finish(window)
|
3407
3445
|
|
3408
3446
|
if __name__ == "__main__":
|
3409
3447
|
run()
|
not1mm/data/new_contest.ui
CHANGED
@@ -297,6 +297,11 @@
|
|
297
297
|
<string>CWT</string>
|
298
298
|
</property>
|
299
299
|
</item>
|
300
|
+
<item>
|
301
|
+
<property name="text">
|
302
|
+
<string>HELVETIA</string>
|
303
|
+
</property>
|
304
|
+
</item>
|
300
305
|
<item>
|
301
306
|
<property name="text">
|
302
307
|
<string>ICWC MST</string>
|
@@ -356,7 +361,7 @@
|
|
356
361
|
</property>
|
357
362
|
<property name="time">
|
358
363
|
<time>
|
359
|
-
<hour>
|
364
|
+
<hour>0</hour>
|
360
365
|
<minute>0</minute>
|
361
366
|
<second>0</second>
|
362
367
|
</time>
|
not1mm/data/splash.png
ADDED
Binary file
|
not1mm/lib/database.py
CHANGED
@@ -567,7 +567,7 @@ class DataBase:
|
|
567
567
|
def fetch_country_band_count(self) -> dict:
|
568
568
|
"""
|
569
569
|
returns dict with count of unique NR.
|
570
|
-
{
|
570
|
+
{cb_count: count}
|
571
571
|
"""
|
572
572
|
try:
|
573
573
|
with sqlite3.connect(self.database) as conn:
|
@@ -581,6 +581,23 @@ class DataBase:
|
|
581
581
|
logger.debug("%s", exception)
|
582
582
|
return {}
|
583
583
|
|
584
|
+
def fetch_country_count(self) -> dict:
|
585
|
+
"""
|
586
|
+
Fetch count of unique countries
|
587
|
+
{dxcc_count: count}
|
588
|
+
"""
|
589
|
+
try:
|
590
|
+
with sqlite3.connect(self.database) as conn:
|
591
|
+
conn.row_factory = self.row_factory
|
592
|
+
cursor = conn.cursor()
|
593
|
+
cursor.execute(
|
594
|
+
f"select count(DISTINCT(CountryPrefix)) as dxcc_count from dxlog where ContestNR = {self.current_contest};"
|
595
|
+
)
|
596
|
+
return cursor.fetchone()
|
597
|
+
except sqlite3.OperationalError as exception:
|
598
|
+
logger.debug("%s", exception)
|
599
|
+
return {}
|
600
|
+
|
584
601
|
def fetch_arrldx_country_band_count(self) -> dict:
|
585
602
|
"""
|
586
603
|
returns dict with count of unique NR.
|
@@ -869,14 +886,14 @@ class DataBase:
|
|
869
886
|
logger.debug("%s", exception)
|
870
887
|
return {}
|
871
888
|
|
872
|
-
def
|
889
|
+
def fetch_mult_count(self, mult: int) -> dict:
|
873
890
|
"""return QSO count"""
|
874
891
|
try:
|
875
892
|
with sqlite3.connect(self.database) as conn:
|
876
893
|
conn.row_factory = self.row_factory
|
877
894
|
cursor = conn.cursor()
|
878
895
|
cursor.execute(
|
879
|
-
f"select count(*) as count from dxlog where
|
896
|
+
f"select count(*) as count from dxlog where IsMultiplier{mult} = 1 and ContestNR = {self.current_contest};"
|
880
897
|
)
|
881
898
|
return cursor.fetchone()
|
882
899
|
except sqlite3.OperationalError as exception:
|
not1mm/lib/ham_utility.py
CHANGED
@@ -373,3 +373,29 @@ def distance_with_latlon(grid1: str, lat2: float, lon2: float) -> float:
|
|
373
373
|
logger.debug("lat1:%d lon1:%d lat2:%d lon2:%d", lat1, lon1, lat2, lon2)
|
374
374
|
# lat2, lon2 = gridtolatlon(grid2)
|
375
375
|
return round(haversine(lon1, lat1, lon2, lat2))
|
376
|
+
|
377
|
+
|
378
|
+
def parse_udc(filename: str) -> dict:
|
379
|
+
"""
|
380
|
+
simply parses a n1mm style udc file and returns a dict with key value pairs.
|
381
|
+
"""
|
382
|
+
|
383
|
+
udc_contest = {}
|
384
|
+
the_good_stuff = False
|
385
|
+
|
386
|
+
try:
|
387
|
+
with open(filename, "r", encoding="utf-8") as file_descriptor:
|
388
|
+
for line in file_descriptor:
|
389
|
+
if "[CONTEST]" in line.upper():
|
390
|
+
the_good_stuff = True
|
391
|
+
continue
|
392
|
+
if "=" in line and the_good_stuff is True:
|
393
|
+
try:
|
394
|
+
key, value = line.split("=")
|
395
|
+
udc_contest[key.strip()] = value.strip()
|
396
|
+
except ValueError:
|
397
|
+
...
|
398
|
+
except FileNotFoundError:
|
399
|
+
logger.debug("UDC file not found.")
|
400
|
+
return {}
|
401
|
+
return udc_contest
|
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"):
|
299
300
|
themode = "PH"
|
301
|
+
if themode in ("FT8", "FT4"):
|
302
|
+
themode = "DG"
|
300
303
|
frequency = str(int(contact.get("Freq", "0"))).rjust(5)
|
301
304
|
|
302
305
|
loggeddate = the_date_and_time[:10]
|
@@ -362,7 +365,15 @@ def ft8_handler(the_packet: dict):
|
|
362
365
|
ALTEREGO.contact["RCV"] = ALTEREGO.receive.text()
|
363
366
|
ALTEREGO.contact["Exchange1"] = the_packet.get("CLASS", "ERR")
|
364
367
|
ALTEREGO.contact["Sect"] = the_packet.get("ARRL_SECT", "ERR")
|
365
|
-
ALTEREGO.contact["Mode"] = "
|
368
|
+
ALTEREGO.contact["Mode"] = the_packet.get("MODE", "ERR")
|
369
|
+
ALTEREGO.contact["Freq"] = round(float(the_packet.get("FREQ", "0.0")) * 1000, 2)
|
370
|
+
ALTEREGO.contact["QSXFreq"] = round(
|
371
|
+
float(the_packet.get("FREQ", "0.0")) * 1000, 2
|
372
|
+
)
|
373
|
+
ALTEREGO.contact["Band"] = get_logged_band(
|
374
|
+
str(int(float(the_packet.get("FREQ", "0.0")) * 1000000))
|
375
|
+
)
|
366
376
|
ALTEREGO.other_1.setText(the_packet.get("CLASS", "ERR"))
|
367
377
|
ALTEREGO.other_2.setText(the_packet.get("ARRL_SECT", "ERR"))
|
368
378
|
print(f"\n{ALTEREGO.contact=}\n")
|
379
|
+
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
|