not1mm 23.4.15__py3-none-any.whl → 23.4.18__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 +115 -15
- not1mm/data/alpha bravo charlie delta.txt +4 -0
- not1mm/data/main.ui +48 -3
- not1mm/data/ssbmacros.txt +24 -0
- not1mm/lib/version.py +1 -1
- not1mm/test.py +30 -105
- {not1mm-23.4.15.dist-info → not1mm-23.4.18.dist-info}/METADATA +20 -3
- {not1mm-23.4.15.dist-info → not1mm-23.4.18.dist-info}/RECORD +12 -10
- {not1mm-23.4.15.dist-info → not1mm-23.4.18.dist-info}/LICENSE +0 -0
- {not1mm-23.4.15.dist-info → not1mm-23.4.18.dist-info}/WHEEL +0 -0
- {not1mm-23.4.15.dist-info → not1mm-23.4.18.dist-info}/entry_points.txt +0 -0
- {not1mm-23.4.15.dist-info → not1mm-23.4.18.dist-info}/top_level.txt +0 -0
not1mm/__main__.py
CHANGED
@@ -29,6 +29,8 @@ from PyQt5.QtWidgets import QFileDialog
|
|
29
29
|
from PyQt5.QtCore import QPoint # pylint: disable=no-name-in-module
|
30
30
|
from PyQt5.QtCore import QDir, QRect, QSize, Qt
|
31
31
|
from PyQt5.QtGui import QFontDatabase # pylint: disable=no-name-in-module
|
32
|
+
import sounddevice as sd
|
33
|
+
import soundfile as sf
|
32
34
|
|
33
35
|
from not1mm.lib.about import About
|
34
36
|
from not1mm.lib.cat_interface import CAT
|
@@ -220,7 +222,7 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
220
222
|
self.actionNew_Database.triggered.connect(self.new_database)
|
221
223
|
self.actionOpen_Database.triggered.connect(self.open_database)
|
222
224
|
|
223
|
-
self.
|
225
|
+
self.actionEdit_Macros.triggered.connect(self.edit_cw_macros)
|
224
226
|
|
225
227
|
self.actionAbout.triggered.connect(self.show_about_dialog)
|
226
228
|
|
@@ -290,6 +292,7 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
290
292
|
self.station = {}
|
291
293
|
self.contact = self.database.empty_contact
|
292
294
|
self.current_op = self.station.get("Call", "")
|
295
|
+
self.make_op_dir()
|
293
296
|
if self.pref.get("contest"):
|
294
297
|
self.load_contest()
|
295
298
|
|
@@ -379,6 +382,7 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
379
382
|
if self.station is None:
|
380
383
|
self.station = {}
|
381
384
|
self.current_op = self.station.get("Call", "")
|
385
|
+
self.make_op_dir()
|
382
386
|
cmd = {}
|
383
387
|
cmd["cmd"] = "NEWDB"
|
384
388
|
self.multicast_interface.send_as_json(cmd)
|
@@ -400,6 +404,7 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
400
404
|
if self.station.get("Call", "") == "":
|
401
405
|
self.edit_station_settings()
|
402
406
|
self.current_op = self.station.get("Call", "")
|
407
|
+
self.make_op_dir()
|
403
408
|
cmd = {}
|
404
409
|
cmd["cmd"] = "NEWDB"
|
405
410
|
self.multicast_interface.send_as_json(cmd)
|
@@ -732,8 +737,8 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
732
737
|
"""Clears the text input fields and sets focus to callsign field."""
|
733
738
|
self.dupe_indicator.hide()
|
734
739
|
self.contact = self.database.empty_contact
|
735
|
-
self.heading_distance.setText("
|
736
|
-
self.dx_entity.setText("
|
740
|
+
self.heading_distance.setText("")
|
741
|
+
self.dx_entity.setText("")
|
737
742
|
if self.contest:
|
738
743
|
mults = self.contest.show_mults(self)
|
739
744
|
qsos = self.contest.show_qso(self)
|
@@ -955,6 +960,7 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
955
960
|
self.settings_dialog.close()
|
956
961
|
if self.current_op == "":
|
957
962
|
self.current_op = self.station.get("Call", "")
|
963
|
+
self.make_op_dir()
|
958
964
|
contest_count = self.database.fetch_all_contests()
|
959
965
|
if len(contest_count) == 0:
|
960
966
|
self.new_contest_dialog()
|
@@ -1040,15 +1046,48 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
1040
1046
|
def process_macro(self, macro: str) -> str:
|
1041
1047
|
"""Process CW macro substitutions"""
|
1042
1048
|
macro = macro.upper()
|
1043
|
-
macro = macro.replace("{MYCALL}", self.station.get("Call"))
|
1049
|
+
macro = macro.replace("{MYCALL}", self.station.get("Call", ""))
|
1044
1050
|
macro = macro.replace("{HISCALL}", self.callsign.text())
|
1045
|
-
|
1051
|
+
if self.radio_state.get("mode") == "CW":
|
1052
|
+
macro = macro.replace("{SNT}", self.sent.text().replace("9", "n"))
|
1053
|
+
else:
|
1054
|
+
macro = macro.replace("{SNT}", self.sent.text())
|
1046
1055
|
macro = macro.replace("{SENTNR}", self.other_1.text())
|
1047
1056
|
return macro
|
1048
1057
|
|
1058
|
+
def voice_string(self, the_string: str) -> None:
|
1059
|
+
"""voices string using nato phonetics"""
|
1060
|
+
logger.debug("Voicing: %s", the_string)
|
1061
|
+
op_path = Path(DATA_PATH) / self.current_op
|
1062
|
+
if "[" in the_string:
|
1063
|
+
sub_string = the_string.strip("[]").lower()
|
1064
|
+
filename = f"{str(op_path)}/{sub_string}.wav"
|
1065
|
+
if Path(filename).is_file():
|
1066
|
+
logger.debug("Voicing: %s", filename)
|
1067
|
+
data, fs = sf.read(filename, dtype="float32")
|
1068
|
+
|
1069
|
+
sd.play(data, fs)
|
1070
|
+
_status = sd.wait()
|
1071
|
+
return
|
1072
|
+
|
1073
|
+
for letter in the_string.lower():
|
1074
|
+
if letter in "abcdefghijklmnopqrstuvwxyz 1234567890":
|
1075
|
+
if letter == " ":
|
1076
|
+
letter = "space"
|
1077
|
+
filename = f"{str(op_path)}/{letter}.wav"
|
1078
|
+
if Path(filename).is_file():
|
1079
|
+
logger.debug("Voicing: %s", filename)
|
1080
|
+
data, fs = sf.read(filename, dtype="float32")
|
1081
|
+
|
1082
|
+
sd.play(data, fs)
|
1083
|
+
_status = sd.wait()
|
1084
|
+
|
1049
1085
|
def sendf1(self):
|
1050
1086
|
"""stub"""
|
1051
1087
|
logger.debug("F1 Clicked")
|
1088
|
+
if self.radio_state.get("mode") in ["USB", "SSB"]:
|
1089
|
+
self.voice_string(self.process_macro(self.F1.toolTip()))
|
1090
|
+
return
|
1052
1091
|
if self.cw:
|
1053
1092
|
# if self.preference.get("send_n1mm_packets"):
|
1054
1093
|
# self.n1mm.radio_info["FunctionKeyCaption"] = self.F1.text()
|
@@ -1057,66 +1096,99 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
1057
1096
|
def sendf2(self):
|
1058
1097
|
"""stub"""
|
1059
1098
|
logger.debug("F2 Clicked")
|
1099
|
+
if self.radio_state.get("mode") in ["USB", "SSB"]:
|
1100
|
+
self.voice_string(self.process_macro(self.F2.toolTip()))
|
1101
|
+
return
|
1060
1102
|
if self.cw:
|
1061
1103
|
self.cw.sendcw(self.process_macro(self.F2.toolTip()))
|
1062
1104
|
|
1063
1105
|
def sendf3(self):
|
1064
1106
|
"""stub"""
|
1065
1107
|
logger.debug("F3 Clicked")
|
1108
|
+
if self.radio_state.get("mode") in ["USB", "SSB"]:
|
1109
|
+
self.voice_string(self.process_macro(self.F3.toolTip()))
|
1110
|
+
return
|
1066
1111
|
if self.cw:
|
1067
1112
|
self.cw.sendcw(self.process_macro(self.F3.toolTip()))
|
1068
1113
|
|
1069
1114
|
def sendf4(self):
|
1070
1115
|
"""stub"""
|
1071
1116
|
logger.debug("F4 Clicked")
|
1117
|
+
if self.radio_state.get("mode") in ["USB", "SSB"]:
|
1118
|
+
self.voice_string(self.process_macro(self.F4.toolTip()))
|
1119
|
+
return
|
1072
1120
|
if self.cw:
|
1073
1121
|
self.cw.sendcw(self.process_macro(self.F4.toolTip()))
|
1074
1122
|
|
1075
1123
|
def sendf5(self):
|
1076
1124
|
"""stub"""
|
1077
1125
|
logger.debug("F5 Clicked")
|
1126
|
+
if self.radio_state.get("mode") in ["USB", "SSB"]:
|
1127
|
+
self.voice_string(self.process_macro(self.F5.toolTip()))
|
1128
|
+
return
|
1078
1129
|
if self.cw:
|
1079
1130
|
self.cw.sendcw(self.process_macro(self.F5.toolTip()))
|
1080
1131
|
|
1081
1132
|
def sendf6(self):
|
1082
1133
|
"""stub"""
|
1083
1134
|
logger.debug("F6 Clicked")
|
1135
|
+
if self.radio_state.get("mode") in ["USB", "SSB"]:
|
1136
|
+
self.voice_string(self.process_macro(self.F6.toolTip()))
|
1137
|
+
return
|
1084
1138
|
if self.cw:
|
1085
1139
|
self.cw.sendcw(self.process_macro(self.F6.toolTip()))
|
1086
1140
|
|
1087
1141
|
def sendf7(self):
|
1088
1142
|
"""stub"""
|
1089
1143
|
logger.debug("F7 Clicked")
|
1144
|
+
if self.radio_state.get("mode") in ["USB", "SSB"]:
|
1145
|
+
self.voice_string(self.process_macro(self.F7.toolTip()))
|
1146
|
+
return
|
1090
1147
|
if self.cw:
|
1091
1148
|
self.cw.sendcw(self.process_macro(self.F7.toolTip()))
|
1092
1149
|
|
1093
1150
|
def sendf8(self):
|
1094
1151
|
"""stub"""
|
1095
1152
|
logger.debug("F8 Clicked")
|
1153
|
+
if self.radio_state.get("mode") in ["USB", "SSB"]:
|
1154
|
+
self.voice_string(self.process_macro(self.F8.toolTip()))
|
1155
|
+
return
|
1096
1156
|
if self.cw:
|
1097
1157
|
self.cw.sendcw(self.process_macro(self.F8.toolTip()))
|
1098
1158
|
|
1099
1159
|
def sendf9(self):
|
1100
1160
|
"""stub"""
|
1101
1161
|
logger.debug("F9 Clicked")
|
1162
|
+
if self.radio_state.get("mode") in ["USB", "SSB"]:
|
1163
|
+
self.voice_string(self.process_macro(self.F9.toolTip()))
|
1164
|
+
return
|
1102
1165
|
if self.cw:
|
1103
1166
|
self.cw.sendcw(self.process_macro(self.F9.toolTip()))
|
1104
1167
|
|
1105
1168
|
def sendf10(self):
|
1106
1169
|
"""stub"""
|
1107
1170
|
logger.debug("F10 Clicked")
|
1171
|
+
if self.radio_state.get("mode") in ["USB", "SSB"]:
|
1172
|
+
self.voice_string(self.process_macro(self.F10.toolTip()))
|
1173
|
+
return
|
1108
1174
|
if self.cw:
|
1109
1175
|
self.cw.sendcw(self.process_macro(self.F10.toolTip()))
|
1110
1176
|
|
1111
1177
|
def sendf11(self):
|
1112
1178
|
"""stub"""
|
1113
1179
|
logger.debug("F11 Clicked")
|
1180
|
+
if self.radio_state.get("mode") in ["USB", "SSB"]:
|
1181
|
+
self.voice_string(self.process_macro(self.F11.toolTip()))
|
1182
|
+
return
|
1114
1183
|
if self.cw:
|
1115
1184
|
self.cw.sendcw(self.process_macro(self.F11.toolTip()))
|
1116
1185
|
|
1117
1186
|
def sendf12(self):
|
1118
1187
|
"""stub"""
|
1119
1188
|
logger.debug("F12 Clicked")
|
1189
|
+
if self.radio_state.get("mode") in ["USB", "SSB"]:
|
1190
|
+
self.voice_string(self.process_macro(self.F12.toolTip()))
|
1191
|
+
return
|
1120
1192
|
if self.cw:
|
1121
1193
|
self.cw.sendcw(self.process_macro(self.F12.toolTip()))
|
1122
1194
|
|
@@ -1380,6 +1452,7 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
1380
1452
|
self.rig_control.set_mode("CW")
|
1381
1453
|
self.set_window_title()
|
1382
1454
|
self.clearinputs()
|
1455
|
+
self.read_cw_macros()
|
1383
1456
|
return
|
1384
1457
|
if stripped_text == "RTTY":
|
1385
1458
|
self.setmode("RTTY")
|
@@ -1401,6 +1474,7 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
1401
1474
|
if self.rig_control:
|
1402
1475
|
self.rig_control.set_mode(self.radio_state.get("mode"))
|
1403
1476
|
self.clearinputs()
|
1477
|
+
self.read_cw_macros()
|
1404
1478
|
return
|
1405
1479
|
if stripped_text == "OPON":
|
1406
1480
|
self.get_opon()
|
@@ -1468,7 +1542,8 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
1468
1542
|
f"{primary_pfx}: {continent}/{entity} cq:{cq} itu:{itu}"
|
1469
1543
|
)
|
1470
1544
|
if len(callsign) > 2:
|
1471
|
-
self.contest
|
1545
|
+
if self.contest:
|
1546
|
+
self.contest.prefill(self)
|
1472
1547
|
|
1473
1548
|
def check_callsign2(self, callsign):
|
1474
1549
|
"""Check call once entered"""
|
@@ -1544,9 +1619,26 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
1544
1619
|
"""Save new OP"""
|
1545
1620
|
if self.opon_dialog.NewOperator.text():
|
1546
1621
|
self.current_op = self.opon_dialog.NewOperator.text().upper()
|
1547
|
-
|
1548
1622
|
self.opon_dialog.close()
|
1549
1623
|
logger.debug("New Op: %s", self.current_op)
|
1624
|
+
self.make_op_dir()
|
1625
|
+
|
1626
|
+
def make_op_dir(self):
|
1627
|
+
"""Create OP directory if it does not exist."""
|
1628
|
+
if self.current_op:
|
1629
|
+
op_path = Path(DATA_PATH) / self.current_op
|
1630
|
+
logger.debug("op_path: %s", str(op_path))
|
1631
|
+
if op_path.is_dir() is False:
|
1632
|
+
logger.debug("Creating Op Directory: %s", str(op_path))
|
1633
|
+
os.mkdir(str(op_path))
|
1634
|
+
if op_path.is_dir():
|
1635
|
+
source_path = Path(WORKING_PATH) / "data" / "phonetics"
|
1636
|
+
logger.debug("source_path: %s", str(source_path))
|
1637
|
+
for child in source_path.iterdir():
|
1638
|
+
destination_file = op_path / child.name
|
1639
|
+
logger.debug("Destination: %s", str(destination_file))
|
1640
|
+
if destination_file.is_file() is False:
|
1641
|
+
destination_file.write_bytes(child.read_bytes())
|
1550
1642
|
|
1551
1643
|
def poll_radio(self):
|
1552
1644
|
"""stub"""
|
@@ -1572,10 +1664,15 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
1572
1664
|
"""
|
1573
1665
|
Calls the default text editor to edit the CW macro file.
|
1574
1666
|
"""
|
1575
|
-
|
1667
|
+
# FIXME
|
1668
|
+
if self.radio_state.get("mode") == "CW":
|
1669
|
+
macro_file = "/cwmacros.txt"
|
1670
|
+
else:
|
1671
|
+
macro_file = "/ssbmacros.txt"
|
1672
|
+
if not Path(DATA_PATH + macro_file).exists():
|
1576
1673
|
logger.debug("read_cw_macros: copying default macro file.")
|
1577
|
-
copyfile(WORKING_PATH + "/data
|
1578
|
-
os.system(f"xdg-open {DATA_PATH}
|
1674
|
+
copyfile(WORKING_PATH + "/data" + macro_file, DATA_PATH + macro_file)
|
1675
|
+
os.system(f"xdg-open {DATA_PATH}{macro_file}")
|
1579
1676
|
self.read_cw_macros()
|
1580
1677
|
|
1581
1678
|
def read_cw_macros(self) -> None:
|
@@ -1585,12 +1682,15 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
1585
1682
|
temp directory this is running from... In theory.
|
1586
1683
|
"""
|
1587
1684
|
|
1588
|
-
if
|
1685
|
+
if self.radio_state.get("mode") == "CW":
|
1686
|
+
macro_file = "/cwmacros.txt"
|
1687
|
+
else:
|
1688
|
+
macro_file = "/ssbmacros.txt"
|
1689
|
+
|
1690
|
+
if not Path(DATA_PATH + macro_file).exists():
|
1589
1691
|
logger.debug("read_cw_macros: copying default macro file.")
|
1590
|
-
copyfile(WORKING_PATH + "/data
|
1591
|
-
with open(
|
1592
|
-
DATA_PATH + "/cwmacros.txt", "r", encoding="utf-8"
|
1593
|
-
) as file_descriptor:
|
1692
|
+
copyfile(WORKING_PATH + "/data" + macro_file, DATA_PATH + macro_file)
|
1693
|
+
with open(DATA_PATH + macro_file, "r", encoding="utf-8") as file_descriptor:
|
1594
1694
|
for line in file_descriptor:
|
1595
1695
|
try:
|
1596
1696
|
mode, fkey, buttonname, cwtext = line.split("|")
|
not1mm/data/main.ui
CHANGED
@@ -1023,7 +1023,7 @@
|
|
1023
1023
|
<addaction name="actionConfiguration_Settings"/>
|
1024
1024
|
<addaction name="actionStationSettings"/>
|
1025
1025
|
<addaction name="separator"/>
|
1026
|
-
<addaction name="
|
1026
|
+
<addaction name="actionEdit_Macros"/>
|
1027
1027
|
</widget>
|
1028
1028
|
<widget class="QMenu" name="menuHelp">
|
1029
1029
|
<property name="title">
|
@@ -1225,6 +1225,11 @@
|
|
1225
1225
|
<property name="text">
|
1226
1226
|
<string>Generate Cabrillo</string>
|
1227
1227
|
</property>
|
1228
|
+
<property name="font">
|
1229
|
+
<font>
|
1230
|
+
<family>JetBrains Mono</family>
|
1231
|
+
</font>
|
1232
|
+
</property>
|
1228
1233
|
<property name="autoRepeat">
|
1229
1234
|
<bool>false</bool>
|
1230
1235
|
</property>
|
@@ -1239,6 +1244,11 @@
|
|
1239
1244
|
<property name="text">
|
1240
1245
|
<string>Log Window</string>
|
1241
1246
|
</property>
|
1247
|
+
<property name="font">
|
1248
|
+
<font>
|
1249
|
+
<family>JetBrains Mono</family>
|
1250
|
+
</font>
|
1251
|
+
</property>
|
1242
1252
|
<property name="autoRepeat">
|
1243
1253
|
<bool>false</bool>
|
1244
1254
|
</property>
|
@@ -1253,6 +1263,11 @@
|
|
1253
1263
|
<property name="text">
|
1254
1264
|
<string>Generate ADIF</string>
|
1255
1265
|
</property>
|
1266
|
+
<property name="font">
|
1267
|
+
<font>
|
1268
|
+
<family>JetBrains Mono</family>
|
1269
|
+
</font>
|
1270
|
+
</property>
|
1256
1271
|
<property name="shortcut">
|
1257
1272
|
<string/>
|
1258
1273
|
</property>
|
@@ -1270,6 +1285,11 @@
|
|
1270
1285
|
<property name="text">
|
1271
1286
|
<string>Recalculate Mults</string>
|
1272
1287
|
</property>
|
1288
|
+
<property name="font">
|
1289
|
+
<font>
|
1290
|
+
<family>JetBrains Mono</family>
|
1291
|
+
</font>
|
1292
|
+
</property>
|
1273
1293
|
<property name="autoRepeat">
|
1274
1294
|
<bool>false</bool>
|
1275
1295
|
</property>
|
@@ -1284,6 +1304,11 @@
|
|
1284
1304
|
<property name="text">
|
1285
1305
|
<string>New Contest</string>
|
1286
1306
|
</property>
|
1307
|
+
<property name="font">
|
1308
|
+
<font>
|
1309
|
+
<family>JetBrains Mono</family>
|
1310
|
+
</font>
|
1311
|
+
</property>
|
1287
1312
|
<property name="autoRepeat">
|
1288
1313
|
<bool>false</bool>
|
1289
1314
|
</property>
|
@@ -1298,6 +1323,11 @@
|
|
1298
1323
|
<property name="text">
|
1299
1324
|
<string>New Database</string>
|
1300
1325
|
</property>
|
1326
|
+
<property name="font">
|
1327
|
+
<font>
|
1328
|
+
<family>JetBrains Mono</family>
|
1329
|
+
</font>
|
1330
|
+
</property>
|
1301
1331
|
<property name="autoRepeat">
|
1302
1332
|
<bool>false</bool>
|
1303
1333
|
</property>
|
@@ -1312,6 +1342,11 @@
|
|
1312
1342
|
<property name="text">
|
1313
1343
|
<string>Open Database</string>
|
1314
1344
|
</property>
|
1345
|
+
<property name="font">
|
1346
|
+
<font>
|
1347
|
+
<family>JetBrains Mono</family>
|
1348
|
+
</font>
|
1349
|
+
</property>
|
1315
1350
|
<property name="autoRepeat">
|
1316
1351
|
<bool>false</bool>
|
1317
1352
|
</property>
|
@@ -1326,6 +1361,11 @@
|
|
1326
1361
|
<property name="text">
|
1327
1362
|
<string>Open Contest</string>
|
1328
1363
|
</property>
|
1364
|
+
<property name="font">
|
1365
|
+
<font>
|
1366
|
+
<family>JetBrains Mono</family>
|
1367
|
+
</font>
|
1368
|
+
</property>
|
1329
1369
|
<property name="autoRepeat">
|
1330
1370
|
<bool>false</bool>
|
1331
1371
|
</property>
|
@@ -1336,9 +1376,14 @@
|
|
1336
1376
|
<bool>false</bool>
|
1337
1377
|
</property>
|
1338
1378
|
</action>
|
1339
|
-
<action name="
|
1379
|
+
<action name="actionEdit_Macros">
|
1340
1380
|
<property name="text">
|
1341
|
-
<string>Edit
|
1381
|
+
<string>Edit Macros</string>
|
1382
|
+
</property>
|
1383
|
+
<property name="font">
|
1384
|
+
<font>
|
1385
|
+
<family>JetBrains Mono</family>
|
1386
|
+
</font>
|
1342
1387
|
</property>
|
1343
1388
|
<property name="autoRepeat">
|
1344
1389
|
<bool>false</bool>
|
@@ -0,0 +1,24 @@
|
|
1
|
+
S|F1|MyCall|{MYCALL}
|
2
|
+
S|F2|Exch|{SNT} {SENTNR}
|
3
|
+
S|F3|My NR|{SENTNR}
|
4
|
+
S|F4|empty|
|
5
|
+
S|F5|AGN|[again]
|
6
|
+
S|F6|empty|
|
7
|
+
S|F7|Roger|[roger]
|
8
|
+
S|F8|73|[73]
|
9
|
+
S|F9|empty|
|
10
|
+
S|F10|Call?|[yourcall]
|
11
|
+
S|F11|NR??|[mynumber]
|
12
|
+
S|F12|TU|[thankyou]
|
13
|
+
R|F1|Run CQ|[cq]
|
14
|
+
R|F2|HisCall|{HISCALL}
|
15
|
+
R|F3|Run Exch|{HISCALL} {SNT} {SENTNR}
|
16
|
+
R|F4|Run TU|[thankyouqrz]
|
17
|
+
R|F5|MyCall|{MYCALL}
|
18
|
+
R|F6|MyNR|{SENTNR}
|
19
|
+
R|F7|empty|
|
20
|
+
R|F8|Roger|[roger]
|
21
|
+
R|F9|AGN|[again]
|
22
|
+
R|F10|Call?|[yourcall]
|
23
|
+
R|F11|NR?|[mynumber]
|
24
|
+
R|F12|empty|
|
not1mm/lib/version.py
CHANGED
@@ -1,2 +1,2 @@
|
|
1
1
|
"""It's the version"""
|
2
|
-
__version__ = "23.4.
|
2
|
+
__version__ = "23.4.18"
|
not1mm/test.py
CHANGED
@@ -5,6 +5,8 @@ Email: michael.bridak@gmail.com
|
|
5
5
|
GPL V3
|
6
6
|
"""
|
7
7
|
|
8
|
+
# playing with sound
|
9
|
+
|
8
10
|
# pylint: disable=too-many-lines
|
9
11
|
# pylint: disable=invalid-name
|
10
12
|
# pylint: disable=no-member
|
@@ -12,119 +14,42 @@ GPL V3
|
|
12
14
|
# pylint: disable=c-extension-no-member
|
13
15
|
# pylint: disable=unused-import
|
14
16
|
|
15
|
-
|
16
|
-
import sys
|
17
|
-
import socket
|
18
|
-
import os
|
19
|
-
import logging
|
20
|
-
import threading
|
21
|
-
import uuid
|
22
|
-
import queue
|
23
|
-
import time
|
24
|
-
from itertools import chain
|
25
|
-
|
26
|
-
from json import dumps, loads, JSONDecodeError
|
27
|
-
from datetime import datetime, timedelta
|
28
|
-
from pathlib import Path
|
29
|
-
from shutil import copyfile
|
30
|
-
|
31
|
-
import requests
|
32
|
-
|
33
|
-
try:
|
34
|
-
from not1mm.lib.database import DataBase
|
35
|
-
from not1mm.lib.version import __version__
|
36
|
-
except ModuleNotFoundError:
|
37
|
-
from lib.database import DataBase
|
38
|
-
from lib.version import __version__
|
39
|
-
|
40
|
-
|
41
|
-
class main:
|
42
|
-
"""test"""
|
43
|
-
|
44
|
-
def __init__(self):
|
45
|
-
# create the DB
|
46
|
-
# self.db = DataBase("file::memory:?cache=shared")
|
47
|
-
self.db = DataBase("testdb.db", ".")
|
48
|
-
self.db_object = None
|
49
|
-
|
50
|
-
def run(self):
|
51
|
-
"""run"""
|
52
|
-
# Set persistent values for contact
|
53
|
-
# self.db.empty_contact["app"] = "K6GTELogger"
|
54
|
-
self.db.empty_contact["StationPrefix"] = "K6GTE"
|
55
|
-
self.db.empty_contact["Operator"] = "K6GTE"
|
56
|
-
self.db.empty_contact["ContestName"] = "CQWWCW"
|
57
|
-
self.db.empty_contact["ContestNR"] = "1"
|
58
|
-
# self.db.empty_contact["StationName"] = "20M CW"
|
59
|
-
|
60
|
-
# get clean contact object with persistent changes from above
|
61
|
-
self.db_object = self.db.get_empty()
|
62
|
-
|
63
|
-
# Make changes to save to the DB
|
64
|
-
self.db_object["ID"] = uuid.uuid4().hex
|
65
|
-
self.db_object["TS"] = datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S")
|
66
|
-
self.db_object["Band"] = 14.0
|
67
|
-
self.db_object["QSXFreq"] = 14030.0
|
68
|
-
self.db_object["Freq"] = 14030.0
|
69
|
-
self.db_object["CountryPrefix"] = "K"
|
70
|
-
self.db_object["Continent"] = "NA"
|
71
|
-
self.db_object["SNT"] = "599"
|
72
|
-
self.db_object["SentNr"] = 1
|
73
|
-
self.db_object["RCV"] = "599"
|
74
|
-
self.db_object["NR"] = 37
|
75
|
-
self.db_object["GridSquare"] = "DM13at"
|
76
|
-
self.db_object["Sect"] = "ORG"
|
77
|
-
self.db_object["QTH"] = "Their Home"
|
78
|
-
self.db_object["Name"] = "Russ"
|
79
|
-
self.db_object["Power"] = "100"
|
80
|
-
self.db_object["Points"] = 2
|
81
|
-
self.db_object["Call"] = "K5TUX"
|
82
|
-
self.db_object["Mode"] = "CW"
|
17
|
+
# import threading
|
83
18
|
|
84
|
-
|
85
|
-
|
86
|
-
print(f"\nSaved Data:\n{self.db.fetch_all_contacts_asc()}\n\n")
|
19
|
+
import sounddevice as sd
|
20
|
+
import soundfile as sf
|
87
21
|
|
88
|
-
|
89
|
-
self.db_object["QSXFreq"] = 14027.0
|
90
|
-
self.db_object["Freq"] = 14027.0
|
91
|
-
self.db.change_contact(self.db_object)
|
92
|
-
print(f"\nUpdated Data:\n{self.db.fetch_all_contacts_asc()}\n\n")
|
22
|
+
print(f"{sd.query_devices(kind='output')}")
|
93
23
|
|
94
|
-
|
95
|
-
|
96
|
-
|
24
|
+
# FILENAME = "/home/mbridak/.local/share/not1mm/K6GTE/z.wav"
|
25
|
+
# current_frame = 0
|
26
|
+
# event = threading.Event()
|
97
27
|
|
98
|
-
|
99
|
-
print(f"\nSorted Descending Data:\n{self.db.fetch_all_contacts_desc()}\n\n")
|
28
|
+
callsign = "k6gte 599 org"
|
100
29
|
|
101
|
-
# fetch last record
|
102
|
-
print(f"\nGet last contact:\n{self.db.fetch_last_contact()}\n\n")
|
103
30
|
|
104
|
-
|
105
|
-
|
31
|
+
# def callback(outdata, frames, _time, status):
|
32
|
+
# """docstring"""
|
33
|
+
# global current_frame
|
34
|
+
# if status:
|
35
|
+
# print(status)
|
36
|
+
# chunksize = min(len(data) - current_frame, frames)
|
37
|
+
# outdata[:chunksize] = data[current_frame : current_frame + chunksize]
|
38
|
+
# if chunksize < frames:
|
39
|
+
# outdata[chunksize:] = 0
|
40
|
+
# raise sd.CallbackStop()
|
41
|
+
# current_frame += chunksize
|
106
42
|
|
107
|
-
# fetch all dirty
|
108
|
-
print(f"\nDeleting Contact:\n{self.db.delete_contact(1)}\n\n")
|
109
43
|
|
44
|
+
# data, fs = sf.read(FILENAME)
|
110
45
|
|
111
|
-
logger = logging.getLogger("__main__")
|
112
|
-
handler = logging.StreamHandler()
|
113
|
-
formatter = logging.Formatter(
|
114
|
-
datefmt="%H:%M:%S",
|
115
|
-
fmt="[%(asctime)s] %(levelname)s %(module)s - %(funcName)s Line %(lineno)d:\n%(message)s",
|
116
|
-
)
|
117
|
-
handler.setFormatter(formatter)
|
118
|
-
logger.addHandler(handler)
|
119
46
|
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
logger.warning("debugging off")
|
47
|
+
for letter in callsign:
|
48
|
+
if letter == " ":
|
49
|
+
letter = "space"
|
50
|
+
filename = f"/home/mbridak/.local/share/not1mm/K6GTE/{letter}.wav"
|
51
|
+
print(filename)
|
52
|
+
data, fs = sf.read(filename, dtype="float32")
|
127
53
|
|
128
|
-
|
129
|
-
|
130
|
-
app.run()
|
54
|
+
sd.play(data, fs)
|
55
|
+
_status = sd.wait()
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: not1mm
|
3
|
-
Version: 23.4.
|
3
|
+
Version: 23.4.18
|
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
|
@@ -21,6 +21,8 @@ Requires-Dist: requests
|
|
21
21
|
Requires-Dist: dicttoxml
|
22
22
|
Requires-Dist: xmltodict
|
23
23
|
Requires-Dist: psutil
|
24
|
+
Requires-Dist: sounddevice
|
25
|
+
Requires-Dist: soundfile
|
24
26
|
|
25
27
|
# Not1MM
|
26
28
|
|
@@ -56,6 +58,8 @@ Requires-Dist: psutil
|
|
56
58
|
- [CW Keyer interface](#cw-keyer-interface)
|
57
59
|
- [Hiding screen elements](#hiding-screen-elements)
|
58
60
|
- [Editing macro keys](#editing-macro-keys)
|
61
|
+
- [Macro substitutions](#macro-substitutions)
|
62
|
+
- [Macro use with voice](#macro-use-with-voice)
|
59
63
|
- [cty.dat and QRZ lookups for distance and bearing](#ctydat-and-qrz-lookups-for-distance-and-bearing)
|
60
64
|
- [Other uses for the call field](#other-uses-for-the-call-field)
|
61
65
|
- [Log Display](#log-display)
|
@@ -102,6 +106,7 @@ Feature complete. I'm only one guy, and I'm not what you'd consider to be a cont
|
|
102
106
|
|
103
107
|
## Changes of note
|
104
108
|
|
109
|
+
- [23-4-18] Added voice keying. Fixed a bunch of crashes.
|
105
110
|
- [23-4-15] Corrected tabstops on new contest screen. Changed project state to BETA.
|
106
111
|
- [23-4-12] Dynamic log window columns. Reload settings after changes saved. Altered JIDX displayed log columns.
|
107
112
|
- [23-4-11] Add about dialog. Fix crash when previous working DB is erased. Add CQ WW CW and SSB. When entering station settings, after entering callsign and grid, the cqzone, ituzone, country, latitude and longitude will auto fill.
|
@@ -264,15 +269,27 @@ The your choices will be remembered when you relaunch the program.
|
|
264
269
|
|
265
270
|
## Editing macro keys
|
266
271
|
|
267
|
-
To edit the
|
272
|
+
To edit the macros, choose `File` > `Edit Macros`. This will open your systems registered text editor with current macros loaded. When your done just save the file and close the editor. The file loaded to edit will be determined by your current operating mode.
|
273
|
+
|
274
|
+
### Macro substitutions
|
268
275
|
|
269
276
|
You can include a limited set of substitution instructions.
|
270
277
|
|
271
278
|
- {MYCALL} Sends the station call.
|
272
279
|
- {HISCALL} Send what's in the callsign field.
|
273
|
-
- {SNT} Sends 5nn
|
280
|
+
- {SNT} Sends 5nn (cw) or 599 (ssb)
|
274
281
|
- {SENTNR} Sends whats in the SentNR field.
|
275
282
|
|
283
|
+
### Macro use with voice
|
284
|
+
|
285
|
+
The macros when used with voice, will also accept filenames of WAV files to play, excluding the file extension. The filename must be enclosed by brackets. For example `[CQ]` will play `cq.wav`, `[again]` will play `again.wav`. The wav files are stored in the operators personal data directory. The filenames must be in lowercase. See [Various data file locations](#various-data-file-locations) above for the location of your data files. For me, the macro `[cq]` will play `/home/mbridak/.local/share/not1mm/K6GTE/cq.wav`
|
286
|
+
|
287
|
+
The current wav files in place are not the ones you will want to use. They sound like some little kid. You can use something like Audacity to record new wav files in your own voice.
|
288
|
+
|
289
|
+
Aside from the `[filename]` wav files, there are also NATO phonetic wav files for each letter and number. So if your macro key holds `{HISCALL} {SNT} {SENTNR}` and you have entered K5TUX in callsign field during CQ WW SSB while in CQ Zone 3. You'll here Kilo 5 Tango Uniform X-ray, 5 9 9, 3. Hopefully not in a little kids voice.
|
290
|
+
|
291
|
+
Right now, the sound is played out of the default sound device. This is NOT what we want. I still have to code up the dialog to choose the correct sound device to use.
|
292
|
+
|
276
293
|
## cty.dat and QRZ lookups for distance and bearing
|
277
294
|
|
278
295
|
When a callsign is entered, a look up is first done in a cty.dat file to determin the country of origin, geographic center, cq zone and ITU region. Great circle calculations are done to determin the heading and distance from your gridsquare to the grographic center. This information then displayed at the bottom left.
|
@@ -1,11 +1,12 @@
|
|
1
1
|
not1mm/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
2
|
-
not1mm/__main__.py,sha256=
|
2
|
+
not1mm/__main__.py,sha256=hVYIWuReXv9iqijMXDc4TZizwMwL_AY1S8Ied_yKmks,71628
|
3
3
|
not1mm/logwindow.py,sha256=FSa6jcKx0dn_DDbubyF20LfSb2IxyUZpuvIaumFf4jo,29725
|
4
|
-
not1mm/test.py,sha256=
|
4
|
+
not1mm/test.py,sha256=k1kJefypzE3NBcUpAcGy7jOe7VEwFs_qgGyDRTeYKuE,1263
|
5
5
|
not1mm/data/Combinear.qss,sha256=SKqM0g8GvNXay1ovgtwCw3Egt0eLfN5P3iTREInC1eE,16590
|
6
6
|
not1mm/data/JetBrainsMono-Regular.ttf,sha256=UOHctAKY_PzCGh7zy-6f6egnCcSK0wzmF0csBqO9lDY,203952
|
7
7
|
not1mm/data/MASTER.SCP,sha256=oqUB3Fvy40vU-ra-R3-cvTeBnZWW04Da1Et011UITbI,542666
|
8
8
|
not1mm/data/about.ui,sha256=ll-3ZSghbok0kW9MLSAoo2dW3EV3lM4m4nR-kUJ6oNw,2352
|
9
|
+
not1mm/data/alpha bravo charlie delta.txt,sha256=d5QMmSWEUAe4Rj1XbNjTPLa_5Be4Se6u5LUIqAYidOQ,224
|
9
10
|
not1mm/data/check.png,sha256=UvFOLr8V-79qnjW8wUaGItXk_OSP8m8hqPevs8NDlFY,387
|
10
11
|
not1mm/data/configuration.ui,sha256=eBbNJgwrtkLcyRbvMxOfm9or4YybOC1HXwzU5NvpLHI,30635
|
11
12
|
not1mm/data/contests.sql,sha256=4hmJCDvrbxnA_Y5S4T5o52TZieeFk6QUwFerwlFePNA,89307
|
@@ -19,12 +20,13 @@ not1mm/data/k6gte.not1mm-128.png,sha256=vWqt3Cgsaguj-BBiIoSJApzzhisPxldM8HZQbZ05
|
|
19
20
|
not1mm/data/k6gte.not1mm-32.png,sha256=yucSwzlmqv3NegdWUvPvZzSgP7G22Ky3se8TWRXvzfI,1108
|
20
21
|
not1mm/data/k6gte.not1mm-64.png,sha256=1KQvk0WBckUds79BvIFUt-KdTwQKKvTz6hiJu8MiT68,2152
|
21
22
|
not1mm/data/logwindow.ui,sha256=_-wobHhIjALzCswyXIrqNadnLdc88eay1GNF23a-Qh0,970
|
22
|
-
not1mm/data/main.ui,sha256=
|
23
|
+
not1mm/data/main.ui,sha256=GHQ_7qZnRad4Q_spTHTls2G7vq_EO76nku6hTjEEJ-c,42469
|
23
24
|
not1mm/data/new_contest.ui,sha256=mn0ieMu2WwcZK7PNzZBf_aChb024VIbUejbvJhMygE0,16726
|
24
25
|
not1mm/data/opon.ui,sha256=6r9_6ORGfNqwOnpzQjaJ1tWP_81amuXqLYlx1hHgdME,2018
|
25
26
|
not1mm/data/pickcontest.ui,sha256=_9JFiJw4l-bRRgNDtVg1DpxreylYd_UqEq0wfcr9gJc,1600
|
26
27
|
not1mm/data/reddot.png,sha256=M33jEMoU8W4rQ4_MVyzzKxDPDte1ypKBch5VnUMNLKE,565
|
27
28
|
not1mm/data/settings.ui,sha256=sK5K5AXSPCMpMFIdqjTvgjxz525AjRiYuQLHid8P3hY,35566
|
29
|
+
not1mm/data/ssbmacros.txt,sha256=0Qccj4y0nlK-w5da9a9ti-jILkURtwztoDuL_D0pEJM,470
|
28
30
|
not1mm/lib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
29
31
|
not1mm/lib/about.py,sha256=O9i8ypv2W9KZkPAp2bcRI0J5RgPE5R1vMfU8ZGlok_E,379
|
30
32
|
not1mm/lib/cat_interface.py,sha256=4hJ3HRybf_Izg_O_jQTilgfPrfc6U-2y5-dGC3xV1go,10342
|
@@ -41,7 +43,7 @@ not1mm/lib/n1mm.py,sha256=bK5d21Yfn6xRpQcu2RdpL2zR8lOlRWOadD6Ai4_lFdc,4434
|
|
41
43
|
not1mm/lib/new_contest.py,sha256=mHKNCS3iKOKN-bT9d8ZK3JemThOZFQ0ikfUSS0-ZTpY,354
|
42
44
|
not1mm/lib/select_contest.py,sha256=XQdRUkPAIHIMVsilm82M54b_v9yWpYrZ1nfInJrtZoo,363
|
43
45
|
not1mm/lib/settings.py,sha256=em4A1vQ4EMl53UP0EWeakrotIs2o2nOhjk6H6BJ7oTM,5409
|
44
|
-
not1mm/lib/version.py,sha256=
|
46
|
+
not1mm/lib/version.py,sha256=1-6sWQIQrJbev_9kEVAX_xdffdPsQrYtrILQdlNJjVM,47
|
45
47
|
not1mm/plugins/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
46
48
|
not1mm/plugins/arrl_dx_cw.py,sha256=IMT3ybtVQp3t8GyIkTE4VyX83d4OFld-gI6VDLDPv3I,2521
|
47
49
|
not1mm/plugins/arrl_dx_phone.py,sha256=w23Q6GO0IiVnCY6F9IDtLduIxG6NhChan5dZZdkzzlc,2524
|
@@ -60,9 +62,9 @@ not1mm/plugins/winter_field_day.py,sha256=NVCzdhJrw-F56mMQ7czZNcsS0bMtttks1VOBIy
|
|
60
62
|
not1mm/testing/test.py,sha256=wsEa05WGFGYSYBRa0e-sxTc13tcibUgkPMiypnHRemc,2297
|
61
63
|
testing/test.py,sha256=0jvT_UcvgcAGwkJNnB9PBDmQhLHQl85NQyT5f9GKBuQ,2248
|
62
64
|
testing/text2.py,sha256=ehbwRI6zimJhOzKBRE540IEITzhvssdvMC4yZ7WcjuU,861
|
63
|
-
not1mm-23.4.
|
64
|
-
not1mm-23.4.
|
65
|
-
not1mm-23.4.
|
66
|
-
not1mm-23.4.
|
67
|
-
not1mm-23.4.
|
68
|
-
not1mm-23.4.
|
65
|
+
not1mm-23.4.18.dist-info/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
66
|
+
not1mm-23.4.18.dist-info/METADATA,sha256=t4biScZUhbs2pr0fFhA3Hq0OHPxBqQeQe53TkViczC0,16745
|
67
|
+
not1mm-23.4.18.dist-info/WHEEL,sha256=pkctZYzUS4AYVn6dJ-7367OJZivF2e8RA9b_ZBjif18,92
|
68
|
+
not1mm-23.4.18.dist-info/entry_points.txt,sha256=pMcZk_0dxFgLkcUkF0Q874ojpwOmF3OL6EKw9LgvocM,47
|
69
|
+
not1mm-23.4.18.dist-info/top_level.txt,sha256=-NwUrh4k1kzpOvf720xYWSTKSlr-zNSIz3D_eplrxLs,15
|
70
|
+
not1mm-23.4.18.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|