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 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.actionEdit_CW_Macros.triggered.connect(self.edit_cw_macros)
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("No Heading")
736
- self.dx_entity.setText("dxentity")
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
- macro = macro.replace("{SNT}", self.sent.text().replace("9", "n"))
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.prefill(self)
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
- if not Path(DATA_PATH + "/cwmacros.txt").exists():
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/cwmacros.txt", DATA_PATH + "/cwmacros.txt")
1578
- os.system(f"xdg-open {DATA_PATH}/cwmacros.txt")
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 not Path(DATA_PATH + "/cwmacros.txt").exists():
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/cwmacros.txt", DATA_PATH + "/cwmacros.txt")
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("|")
@@ -0,0 +1,4 @@
1
+ alpha, bravo, charlie, delta, echo, foxtrot, golf, hotel, india, juliet, kilowatt, lima, mike, november, oscar, papa, quebec, romeo, sierra, tango, uniform, victor, whiskey, xray, yankee, zulu
2
+
3
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
4
+
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="actionEdit_CW_Macros"/>
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="actionEdit_CW_Macros">
1379
+ <action name="actionEdit_Macros">
1340
1380
  <property name="text">
1341
- <string>Edit CW Macros</string>
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.15"
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
- from math import radians, sin, cos, atan2, sqrt, asin, pi
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
- # Save the contact to the DB
85
- self.db.log_contact(self.db_object)
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
- # save changes
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
- # get unique id of record
95
- # uniqid = self.db.get_unique_id(1)
96
- # print(f"Unique ID: {uniqid}")
24
+ # FILENAME = "/home/mbridak/.local/share/not1mm/K6GTE/z.wav"
25
+ # current_frame = 0
26
+ # event = threading.Event()
97
27
 
98
- # fetch descending
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
- # fetch all dirty
105
- # print(f"\nGet all marked dirty:\n{self.db.fetch_all_dirty_contacts()}\n\n")
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
- if Path("./debug").exists():
121
- # if True:
122
- logger.setLevel(logging.DEBUG)
123
- logger.debug("debugging on")
124
- else:
125
- logger.setLevel(logging.WARNING)
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
- if __name__ == "__main__":
129
- app = main()
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.15
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 CW macros, choose `File` > `Edit CW Macros`. This will open your systems registered text editor with current macros loaded. When your done just save the file and close the editor.
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=oTy_DOwsdOAmq4AbgZdRcqdJLnLemCo3_ICjHYSWsAM,67232
2
+ not1mm/__main__.py,sha256=hVYIWuReXv9iqijMXDc4TZizwMwL_AY1S8Ied_yKmks,71628
3
3
  not1mm/logwindow.py,sha256=FSa6jcKx0dn_DDbubyF20LfSb2IxyUZpuvIaumFf4jo,29725
4
- not1mm/test.py,sha256=sCAd6OZQlakGm2gMbjhwZ17UyI-Y5L2CCqsYymx7IPQ,3906
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=WM4BnZaY5kzoK8S26jEpKbMhzbJOFgo5cHAHmLS18EQ,41569
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=kUgprEqtL-INcY49zexn8gdKCZBvUF2DB-ssMsKRX0U,47
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.15.dist-info/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
64
- not1mm-23.4.15.dist-info/METADATA,sha256=NCsOh5yz_e3v1QQnBfqPHqk3EEnNTazi9gwzRQ7br-0,15190
65
- not1mm-23.4.15.dist-info/WHEEL,sha256=pkctZYzUS4AYVn6dJ-7367OJZivF2e8RA9b_ZBjif18,92
66
- not1mm-23.4.15.dist-info/entry_points.txt,sha256=pMcZk_0dxFgLkcUkF0Q874ojpwOmF3OL6EKw9LgvocM,47
67
- not1mm-23.4.15.dist-info/top_level.txt,sha256=-NwUrh4k1kzpOvf720xYWSTKSlr-zNSIz3D_eplrxLs,15
68
- not1mm-23.4.15.dist-info/RECORD,,
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,,