not1mm 23.4.13__tar.gz → 23.4.18__tar.gz

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.
Files changed (77) hide show
  1. {not1mm-23.4.13 → not1mm-23.4.18}/PKG-INFO +46 -22
  2. {not1mm-23.4.13 → not1mm-23.4.18}/README.md +44 -20
  3. {not1mm-23.4.13 → not1mm-23.4.18}/not1mm/__main__.py +115 -15
  4. not1mm-23.4.18/not1mm/data/alpha bravo charlie delta.txt +4 -0
  5. {not1mm-23.4.13 → not1mm-23.4.18}/not1mm/data/configuration.ui +64 -10
  6. {not1mm-23.4.13 → not1mm-23.4.18}/not1mm/data/main.ui +48 -3
  7. {not1mm-23.4.13 → not1mm-23.4.18}/not1mm/data/new_contest.ui +16 -1
  8. {not1mm-23.4.13 → not1mm-23.4.18}/not1mm/data/settings.ui +1 -1
  9. not1mm-23.4.18/not1mm/data/ssbmacros.txt +24 -0
  10. not1mm-23.4.18/not1mm/lib/version.py +2 -0
  11. not1mm-23.4.18/not1mm/test.py +55 -0
  12. {not1mm-23.4.13 → not1mm-23.4.18}/not1mm.egg-info/PKG-INFO +46 -22
  13. {not1mm-23.4.13 → not1mm-23.4.18}/not1mm.egg-info/SOURCES.txt +2 -0
  14. {not1mm-23.4.13 → not1mm-23.4.18}/not1mm.egg-info/requires.txt +2 -0
  15. {not1mm-23.4.13 → not1mm-23.4.18}/pyproject.toml +4 -2
  16. not1mm-23.4.13/not1mm/lib/version.py +0 -2
  17. not1mm-23.4.13/not1mm/test.py +0 -130
  18. {not1mm-23.4.13 → not1mm-23.4.18}/LICENSE +0 -0
  19. {not1mm-23.4.13 → not1mm-23.4.18}/not1mm/__init__.py +0 -0
  20. {not1mm-23.4.13 → not1mm-23.4.18}/not1mm/data/Combinear.qss +0 -0
  21. {not1mm-23.4.13 → not1mm-23.4.18}/not1mm/data/JetBrainsMono-Regular.ttf +0 -0
  22. {not1mm-23.4.13 → not1mm-23.4.18}/not1mm/data/MASTER.SCP +0 -0
  23. {not1mm-23.4.13 → not1mm-23.4.18}/not1mm/data/about.ui +0 -0
  24. {not1mm-23.4.13 → not1mm-23.4.18}/not1mm/data/check.png +0 -0
  25. {not1mm-23.4.13 → not1mm-23.4.18}/not1mm/data/contests.sql +0 -0
  26. {not1mm-23.4.13 → not1mm-23.4.18}/not1mm/data/cty.json +0 -0
  27. {not1mm-23.4.13 → not1mm-23.4.18}/not1mm/data/cwmacros.txt +0 -0
  28. {not1mm-23.4.13 → not1mm-23.4.18}/not1mm/data/editcontact.ui +0 -0
  29. {not1mm-23.4.13 → not1mm-23.4.18}/not1mm/data/editmacro.ui +0 -0
  30. {not1mm-23.4.13 → not1mm-23.4.18}/not1mm/data/greendot.png +0 -0
  31. {not1mm-23.4.13 → not1mm-23.4.18}/not1mm/data/k6gte-not1mm.desktop +0 -0
  32. {not1mm-23.4.13 → not1mm-23.4.18}/not1mm/data/k6gte.not1mm-128.png +0 -0
  33. {not1mm-23.4.13 → not1mm-23.4.18}/not1mm/data/k6gte.not1mm-32.png +0 -0
  34. {not1mm-23.4.13 → not1mm-23.4.18}/not1mm/data/k6gte.not1mm-64.png +0 -0
  35. {not1mm-23.4.13 → not1mm-23.4.18}/not1mm/data/logwindow.ui +0 -0
  36. {not1mm-23.4.13 → not1mm-23.4.18}/not1mm/data/opon.ui +0 -0
  37. {not1mm-23.4.13 → not1mm-23.4.18}/not1mm/data/pickcontest.ui +0 -0
  38. {not1mm-23.4.13 → not1mm-23.4.18}/not1mm/data/reddot.png +0 -0
  39. {not1mm-23.4.13 → not1mm-23.4.18}/not1mm/lib/__init__.py +0 -0
  40. {not1mm-23.4.13 → not1mm-23.4.18}/not1mm/lib/about.py +0 -0
  41. {not1mm-23.4.13 → not1mm-23.4.18}/not1mm/lib/cat_interface.py +0 -0
  42. {not1mm-23.4.13 → not1mm-23.4.18}/not1mm/lib/cwinterface.py +0 -0
  43. {not1mm-23.4.13 → not1mm-23.4.18}/not1mm/lib/database.py +0 -0
  44. {not1mm-23.4.13 → not1mm-23.4.18}/not1mm/lib/edit_contact.py +0 -0
  45. {not1mm-23.4.13 → not1mm-23.4.18}/not1mm/lib/edit_macro.py +0 -0
  46. {not1mm-23.4.13 → not1mm-23.4.18}/not1mm/lib/edit_opon.py +0 -0
  47. {not1mm-23.4.13 → not1mm-23.4.18}/not1mm/lib/edit_station.py +0 -0
  48. {not1mm-23.4.13 → not1mm-23.4.18}/not1mm/lib/ham_utility.py +0 -0
  49. {not1mm-23.4.13 → not1mm-23.4.18}/not1mm/lib/lookup.py +0 -0
  50. {not1mm-23.4.13 → not1mm-23.4.18}/not1mm/lib/multicast.py +0 -0
  51. {not1mm-23.4.13 → not1mm-23.4.18}/not1mm/lib/n1mm.py +0 -0
  52. {not1mm-23.4.13 → not1mm-23.4.18}/not1mm/lib/new_contest.py +0 -0
  53. {not1mm-23.4.13 → not1mm-23.4.18}/not1mm/lib/select_contest.py +0 -0
  54. {not1mm-23.4.13 → not1mm-23.4.18}/not1mm/lib/settings.py +0 -0
  55. {not1mm-23.4.13 → not1mm-23.4.18}/not1mm/logwindow.py +0 -0
  56. {not1mm-23.4.13 → not1mm-23.4.18}/not1mm/plugins/__init__.py +0 -0
  57. {not1mm-23.4.13 → not1mm-23.4.18}/not1mm/plugins/arrl_dx_cw.py +0 -0
  58. {not1mm-23.4.13 → not1mm-23.4.18}/not1mm/plugins/arrl_dx_phone.py +0 -0
  59. {not1mm-23.4.13 → not1mm-23.4.18}/not1mm/plugins/arrl_field_day.py +0 -0
  60. {not1mm-23.4.13 → not1mm-23.4.18}/not1mm/plugins/arrl_rtty_ru.py +0 -0
  61. {not1mm-23.4.13 → not1mm-23.4.18}/not1mm/plugins/arrl_ss_cw.py +0 -0
  62. {not1mm-23.4.13 → not1mm-23.4.18}/not1mm/plugins/arrl_ss_phone.py +0 -0
  63. {not1mm-23.4.13 → not1mm-23.4.18}/not1mm/plugins/cq_wpx_cw.py +0 -0
  64. {not1mm-23.4.13 → not1mm-23.4.18}/not1mm/plugins/cq_wpx_ssb.py +0 -0
  65. {not1mm-23.4.13 → not1mm-23.4.18}/not1mm/plugins/cq_ww_cw.py +0 -0
  66. {not1mm-23.4.13 → not1mm-23.4.18}/not1mm/plugins/cq_ww_ssb.py +0 -0
  67. {not1mm-23.4.13 → not1mm-23.4.18}/not1mm/plugins/general_logging.py +0 -0
  68. {not1mm-23.4.13 → not1mm-23.4.18}/not1mm/plugins/jidx_cw.py +0 -0
  69. {not1mm-23.4.13 → not1mm-23.4.18}/not1mm/plugins/jidx_ph.py +0 -0
  70. {not1mm-23.4.13 → not1mm-23.4.18}/not1mm/plugins/winter_field_day.py +0 -0
  71. {not1mm-23.4.13 → not1mm-23.4.18}/not1mm/testing/test.py +0 -0
  72. {not1mm-23.4.13 → not1mm-23.4.18}/not1mm.egg-info/dependency_links.txt +0 -0
  73. {not1mm-23.4.13 → not1mm-23.4.18}/not1mm.egg-info/entry_points.txt +0 -0
  74. {not1mm-23.4.13 → not1mm-23.4.18}/not1mm.egg-info/top_level.txt +0 -0
  75. {not1mm-23.4.13 → not1mm-23.4.18}/setup.cfg +0 -0
  76. {not1mm-23.4.13 → not1mm-23.4.18}/testing/test.py +0 -0
  77. {not1mm-23.4.13 → not1mm-23.4.18}/testing/text2.py +0 -0
@@ -1,12 +1,12 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: not1mm
3
- Version: 23.4.13
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
7
7
  Project-URL: Bug Tracker, https://github.com/mbridak/not1mm/issues
8
8
  Classifier: Programming Language :: Python :: 3
9
- Classifier: Development Status :: 1 - Planning
9
+ Classifier: Development Status :: 4 - Beta
10
10
  Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
11
11
  Classifier: Environment :: X11 Applications :: Qt
12
12
  Classifier: Operating System :: POSIX :: Linux
@@ -43,11 +43,16 @@ License-File: LICENSE
43
43
  - [Revisiting an old friend](#revisiting-an-old-friend)
44
44
  - [Station Settings dialog](#station-settings-dialog)
45
45
  - [Changing station information](#changing-station-information)
46
- - [Adding a contest](#adding-a-contest)
47
- - [Selecting an added contest as the current contest](#selecting-an-added-contest-as-the-current-contest)
46
+ - [Adding a contest to the current dababase](#adding-a-contest-to-the-current-dababase)
47
+ - [Selecting an existing contest as the current contest](#selecting-an-existing-contest-as-the-current-contest)
48
48
  - [Configuration Settings](#configuration-settings)
49
+ - [Lookup](#lookup)
50
+ - [CAT](#cat)
51
+ - [CW Keyer interface](#cw-keyer-interface)
49
52
  - [Hiding screen elements](#hiding-screen-elements)
50
53
  - [Editing macro keys](#editing-macro-keys)
54
+ - [Macro substitutions](#macro-substitutions)
55
+ - [Macro use with voice](#macro-use-with-voice)
51
56
  - [cty.dat and QRZ lookups for distance and bearing](#ctydat-and-qrz-lookups-for-distance-and-bearing)
52
57
  - [Other uses for the call field](#other-uses-for-the-call-field)
53
58
  - [Log Display](#log-display)
@@ -56,8 +61,6 @@ License-File: LICENSE
56
61
  - [Cabrillo](#cabrillo)
57
62
  - [ADIF](#adif)
58
63
  - [Dupe checking](#dupe-checking)
59
- - [CAT](#cat)
60
- - [CW Keyer interface](#cw-keyer-interface)
61
64
 
62
65
  ## What and why is Not1MM
63
66
 
@@ -96,6 +99,8 @@ Feature complete. I'm only one guy, and I'm not what you'd consider to be a cont
96
99
 
97
100
  ## Changes of note
98
101
 
102
+ - [23-4-18] Added voice keying. Fixed a bunch of crashes.
103
+ - [23-4-15] Corrected tabstops on new contest screen. Changed project state to BETA.
99
104
  - [23-4-12] Dynamic log window columns. Reload settings after changes saved. Altered JIDX displayed log columns.
100
105
  - [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.
101
106
  - [23-4-10] Added Configure Settings dialog. Added CW keyer and CAT control settings to Configure Settings dialog.
@@ -174,7 +179,7 @@ not1mm
174
179
 
175
180
  ### Data
176
181
 
177
- If your system has an `XDG_DATA_HOME` environment variable set, the databases can be found there. Otherwise they will be found at `yourhome/.local/share/not1mm`
182
+ If your system has an `XDG_DATA_HOME` environment variable set, the database and CW macro files can be found there. Otherwise they will be found at `yourhome/.local/share/not1mm`
178
183
 
179
184
  ### Config
180
185
 
@@ -196,7 +201,7 @@ You can create a new database by selecting `File` > `New Database` from the main
196
201
 
197
202
  ### Revisiting an old friend
198
203
 
199
- You can select previously created databases by selecting `File` > `Open Database`.
204
+ You can select a previously created databases for use by selecting `File` > `Open Database`.
200
205
 
201
206
  ## Station Settings dialog
202
207
 
@@ -214,13 +219,13 @@ You can fill. You can fill. Everyone look at your keys.
214
219
 
215
220
  Station information can be changed any time by going to `File` > `Station Settings` and editing the information.
216
221
 
217
- ## Adding a contest
222
+ ## Adding a contest to the current dababase
218
223
 
219
224
  Select `File` > `New Contest`
220
225
 
221
226
  ![New Contest Dialog](https://github.com/mbridak/not1mm/raw/master/pic/new_contest.png)
222
227
 
223
- ## Selecting an added contest as the current contest
228
+ ## Selecting an existing contest as the current contest
224
229
 
225
230
  Select `File` > `Open Contest`
226
231
 
@@ -228,10 +233,25 @@ Select `File` > `Open Contest`
228
233
 
229
234
  ## Configuration Settings
230
235
 
231
- Select `File` > `Configuration Settings`
236
+ To setup your CAT control, CW keyer, Callsign lookups, select `File` > `Configuration Settings`
237
+
238
+ The tabs for cloudlog, groups and n1mm are disabled and are for future expansion.
232
239
 
233
240
  ![Configuration Settings screen](https://github.com/mbridak/not1mm/raw/master/pic/configuration_settings.png)
234
241
 
242
+ ### Lookup
243
+
244
+ For callsign lookup, several services are supported. QRZ, HamQTH and HamDB. If your service of choice requires a username and password, Enter it here.
245
+
246
+ ### CAT
247
+
248
+ Under the `CAT` TAB, you can choose either `rigctld` normally with an IP of `127.0.0.1` and a port of `4532`. Or `flrig`, IP normally of `127.0.0.1` and a port of `12345`. `None` is always an option, but is it really?
249
+
250
+ ### CW Keyer interface
251
+
252
+ Under the `CW` TAB, There are three options. `cwdaemon`, which normally uses IP `127.0.0.1` and port `6789`. `pywinkeyer` which normally uses IP `127.0.0.1` and port `8000`. Or `None`, if you want to Morse it like it's 1899.
253
+
254
+
235
255
  ## Hiding screen elements
236
256
 
237
257
  You can show or hide certain buttons/indicators by checking and unchecking their boxes under the view menu. You can then resize the screen to make it more compact.
@@ -242,15 +262,27 @@ The your choices will be remembered when you relaunch the program.
242
262
 
243
263
  ## Editing macro keys
244
264
 
245
- 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.
265
+ 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.
266
+
267
+ ### Macro substitutions
246
268
 
247
269
  You can include a limited set of substitution instructions.
248
270
 
249
271
  - {MYCALL} Sends the station call.
250
272
  - {HISCALL} Send what's in the callsign field.
251
- - {SNT} Sends 5nn
273
+ - {SNT} Sends 5nn (cw) or 599 (ssb)
252
274
  - {SENTNR} Sends whats in the SentNR field.
253
275
 
276
+ ### Macro use with voice
277
+
278
+ 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`
279
+
280
+ 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.
281
+
282
+ 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.
283
+
284
+ 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.
285
+
254
286
  ## cty.dat and QRZ lookups for distance and bearing
255
287
 
256
288
  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.
@@ -275,7 +307,7 @@ The Log display gets updated automatically when a contact is entered. The top ha
275
307
 
276
308
  ![Log Display Window](https://github.com/mbridak/not1mm/raw/master/pic/logdisplay.png)
277
309
 
278
- The bottom half of the log displays contacts sorted by what's currently in the call entry field.
310
+ The bottom half of the log displays contacts sorted by what's currently in the call entry field. The columns displayed in the log window are dependant on what contests is currently active.
279
311
 
280
312
  ## Editing a contact
281
313
 
@@ -316,11 +348,3 @@ Boom... ADIF
316
348
  ## Dupe checking
317
349
 
318
350
  Added dupe checking. Big Red 'Dupe' will appear if it's a dupe...
319
-
320
- ## CAT
321
-
322
- Go to `File` > `Configuration Settings`, You will find a `CAT` TAB. You can choose either `rigctld` normally with an IP of `127.0.0.1` and a port of `4532`. Or `flrig`, IP normally of `127.0.0.1` and a port of `12345`. `None` is always an option, but is it really?
323
-
324
- ## CW Keyer interface
325
-
326
- Go to `File` > `Configuration Settings`, You will find a `CW` TAB. There are three options. `cwdaemon`, IP `127.0.0.1`, port `6789`. `pywinkeyer`, IP `127.0.0.1`, port `8000`. And `None` if you want to Morse it like it's 1899.
@@ -24,11 +24,16 @@
24
24
  - [Revisiting an old friend](#revisiting-an-old-friend)
25
25
  - [Station Settings dialog](#station-settings-dialog)
26
26
  - [Changing station information](#changing-station-information)
27
- - [Adding a contest](#adding-a-contest)
28
- - [Selecting an added contest as the current contest](#selecting-an-added-contest-as-the-current-contest)
27
+ - [Adding a contest to the current dababase](#adding-a-contest-to-the-current-dababase)
28
+ - [Selecting an existing contest as the current contest](#selecting-an-existing-contest-as-the-current-contest)
29
29
  - [Configuration Settings](#configuration-settings)
30
+ - [Lookup](#lookup)
31
+ - [CAT](#cat)
32
+ - [CW Keyer interface](#cw-keyer-interface)
30
33
  - [Hiding screen elements](#hiding-screen-elements)
31
34
  - [Editing macro keys](#editing-macro-keys)
35
+ - [Macro substitutions](#macro-substitutions)
36
+ - [Macro use with voice](#macro-use-with-voice)
32
37
  - [cty.dat and QRZ lookups for distance and bearing](#ctydat-and-qrz-lookups-for-distance-and-bearing)
33
38
  - [Other uses for the call field](#other-uses-for-the-call-field)
34
39
  - [Log Display](#log-display)
@@ -37,8 +42,6 @@
37
42
  - [Cabrillo](#cabrillo)
38
43
  - [ADIF](#adif)
39
44
  - [Dupe checking](#dupe-checking)
40
- - [CAT](#cat)
41
- - [CW Keyer interface](#cw-keyer-interface)
42
45
 
43
46
  ## What and why is Not1MM
44
47
 
@@ -77,6 +80,8 @@ Feature complete. I'm only one guy, and I'm not what you'd consider to be a cont
77
80
 
78
81
  ## Changes of note
79
82
 
83
+ - [23-4-18] Added voice keying. Fixed a bunch of crashes.
84
+ - [23-4-15] Corrected tabstops on new contest screen. Changed project state to BETA.
80
85
  - [23-4-12] Dynamic log window columns. Reload settings after changes saved. Altered JIDX displayed log columns.
81
86
  - [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.
82
87
  - [23-4-10] Added Configure Settings dialog. Added CW keyer and CAT control settings to Configure Settings dialog.
@@ -155,7 +160,7 @@ not1mm
155
160
 
156
161
  ### Data
157
162
 
158
- If your system has an `XDG_DATA_HOME` environment variable set, the databases can be found there. Otherwise they will be found at `yourhome/.local/share/not1mm`
163
+ If your system has an `XDG_DATA_HOME` environment variable set, the database and CW macro files can be found there. Otherwise they will be found at `yourhome/.local/share/not1mm`
159
164
 
160
165
  ### Config
161
166
 
@@ -177,7 +182,7 @@ You can create a new database by selecting `File` > `New Database` from the main
177
182
 
178
183
  ### Revisiting an old friend
179
184
 
180
- You can select previously created databases by selecting `File` > `Open Database`.
185
+ You can select a previously created databases for use by selecting `File` > `Open Database`.
181
186
 
182
187
  ## Station Settings dialog
183
188
 
@@ -195,13 +200,13 @@ You can fill. You can fill. Everyone look at your keys.
195
200
 
196
201
  Station information can be changed any time by going to `File` > `Station Settings` and editing the information.
197
202
 
198
- ## Adding a contest
203
+ ## Adding a contest to the current dababase
199
204
 
200
205
  Select `File` > `New Contest`
201
206
 
202
207
  ![New Contest Dialog](https://github.com/mbridak/not1mm/raw/master/pic/new_contest.png)
203
208
 
204
- ## Selecting an added contest as the current contest
209
+ ## Selecting an existing contest as the current contest
205
210
 
206
211
  Select `File` > `Open Contest`
207
212
 
@@ -209,10 +214,25 @@ Select `File` > `Open Contest`
209
214
 
210
215
  ## Configuration Settings
211
216
 
212
- Select `File` > `Configuration Settings`
217
+ To setup your CAT control, CW keyer, Callsign lookups, select `File` > `Configuration Settings`
218
+
219
+ The tabs for cloudlog, groups and n1mm are disabled and are for future expansion.
213
220
 
214
221
  ![Configuration Settings screen](https://github.com/mbridak/not1mm/raw/master/pic/configuration_settings.png)
215
222
 
223
+ ### Lookup
224
+
225
+ For callsign lookup, several services are supported. QRZ, HamQTH and HamDB. If your service of choice requires a username and password, Enter it here.
226
+
227
+ ### CAT
228
+
229
+ Under the `CAT` TAB, you can choose either `rigctld` normally with an IP of `127.0.0.1` and a port of `4532`. Or `flrig`, IP normally of `127.0.0.1` and a port of `12345`. `None` is always an option, but is it really?
230
+
231
+ ### CW Keyer interface
232
+
233
+ Under the `CW` TAB, There are three options. `cwdaemon`, which normally uses IP `127.0.0.1` and port `6789`. `pywinkeyer` which normally uses IP `127.0.0.1` and port `8000`. Or `None`, if you want to Morse it like it's 1899.
234
+
235
+
216
236
  ## Hiding screen elements
217
237
 
218
238
  You can show or hide certain buttons/indicators by checking and unchecking their boxes under the view menu. You can then resize the screen to make it more compact.
@@ -223,15 +243,27 @@ The your choices will be remembered when you relaunch the program.
223
243
 
224
244
  ## Editing macro keys
225
245
 
226
- 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.
246
+ 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.
247
+
248
+ ### Macro substitutions
227
249
 
228
250
  You can include a limited set of substitution instructions.
229
251
 
230
252
  - {MYCALL} Sends the station call.
231
253
  - {HISCALL} Send what's in the callsign field.
232
- - {SNT} Sends 5nn
254
+ - {SNT} Sends 5nn (cw) or 599 (ssb)
233
255
  - {SENTNR} Sends whats in the SentNR field.
234
256
 
257
+ ### Macro use with voice
258
+
259
+ 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`
260
+
261
+ 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.
262
+
263
+ 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.
264
+
265
+ 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.
266
+
235
267
  ## cty.dat and QRZ lookups for distance and bearing
236
268
 
237
269
  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.
@@ -256,7 +288,7 @@ The Log display gets updated automatically when a contact is entered. The top ha
256
288
 
257
289
  ![Log Display Window](https://github.com/mbridak/not1mm/raw/master/pic/logdisplay.png)
258
290
 
259
- The bottom half of the log displays contacts sorted by what's currently in the call entry field.
291
+ The bottom half of the log displays contacts sorted by what's currently in the call entry field. The columns displayed in the log window are dependant on what contests is currently active.
260
292
 
261
293
  ## Editing a contact
262
294
 
@@ -297,11 +329,3 @@ Boom... ADIF
297
329
  ## Dupe checking
298
330
 
299
331
  Added dupe checking. Big Red 'Dupe' will appear if it's a dupe...
300
-
301
- ## CAT
302
-
303
- Go to `File` > `Configuration Settings`, You will find a `CAT` TAB. You can choose either `rigctld` normally with an IP of `127.0.0.1` and a port of `4532`. Or `flrig`, IP normally of `127.0.0.1` and a port of `12345`. `None` is always an option, but is it really?
304
-
305
- ## CW Keyer interface
306
-
307
- Go to `File` > `Configuration Settings`, You will find a `CW` TAB. There are three options. `cwdaemon`, IP `127.0.0.1`, port `6789`. `pywinkeyer`, IP `127.0.0.1`, port `8000`. And `None` if you want to Morse it like it's 1899.
@@ -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
+