not1mm 25.5.25__py3-none-any.whl → 25.5.26__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.
@@ -217,6 +217,11 @@
217
217
  <string>ARI 40 80</string>
218
218
  </property>
219
219
  </item>
220
+ <item>
221
+ <property name="text">
222
+ <string>ARI DX</string>
223
+ </property>
224
+ </item>
220
225
  <item>
221
226
  <property name="text">
222
227
  <string>ARRL 10M</string>
not1mm/lib/version.py CHANGED
@@ -1,4 +1,4 @@
1
1
  """It's the version"""
2
2
 
3
- __version__ = "25.5.25"
3
+ __version__ = "25.5.26"
4
4
 
not1mm/logwindow.py CHANGED
@@ -900,12 +900,13 @@ class LogWindow(QDockWidget):
900
900
  self.get_column("UUID"),
901
901
  QtWidgets.QTableWidgetItem(str(log_item.get("ID", ""))),
902
902
  )
903
- self.generalLog.blockSignals(False)
904
- self.focusedLog.blockSignals(False)
903
+
905
904
  self.generalLog.resizeColumnsToContents()
906
905
  self.generalLog.resizeRowsToContents()
907
906
  self.focusedLog.resizeColumnsToContents()
908
907
  self.focusedLog.resizeRowsToContents()
908
+ self.generalLog.blockSignals(False)
909
+ self.focusedLog.blockSignals(False)
909
910
 
910
911
  def show_like_calls(self, call: str) -> None:
911
912
  """
@@ -0,0 +1,558 @@
1
+ """ARI International DX Contest"""
2
+
3
+ # Status: Active
4
+ # Geographic Focus: Worldwide
5
+ # Participation: Worldwide
6
+ # Awards: Worldwide
7
+ # Mode: Phone, CW, RTTY
8
+ # Bands: 80, 40, 20, 15, 10m
9
+ # Classes: Single Op (CW/SSB/RTTY/Mixed)(Low/High)
10
+ # Single Op Overlays (Italian only): (Rookie/Youth)
11
+ # Multi-Single
12
+ # Multi-Multi
13
+ # SWL
14
+ # Max power: HP: >100 watts
15
+ # LP: 100 watts
16
+ # Exchange: I: RS(T) + 2-letter province
17
+ # non-I: RS(T) + Serial No.
18
+ # Work stations: Once per mode per band
19
+ # QSO Points: 0 points per QSO with same country
20
+ # 1 point per QSO with different country same continent
21
+ # 3 points per QSO with different continent
22
+ # 10 points per QSO with I/IS0/IT9 stations
23
+ # Multipliers: Each Italian province once per band
24
+ # Each DXCC country once per band
25
+ # Score Calculation: Total score = total QSO points x total mults
26
+ # E-mail logs to: (none)
27
+ # Upload log at: https://www.ari.it/contest-hf/ari-international/log-upload.html
28
+ # Mail logs to: (none)
29
+ # Find rules at: https://www.ari.it/
30
+ # Cabrillo name: ARI-DX
31
+
32
+
33
+ # pylint: disable=invalid-name, c-extension-no-member, unused-import, line-too-long
34
+
35
+ import datetime
36
+ import logging
37
+ import platform
38
+
39
+ from pathlib import Path
40
+
41
+ from PyQt6 import QtWidgets
42
+
43
+ from not1mm.lib.plugin_common import gen_adif
44
+ from not1mm.lib.version import __version__
45
+
46
+ logger = logging.getLogger(__name__)
47
+
48
+ EXCHANGE_HINT = "Prov or '#'"
49
+
50
+ name = "ARI International DX"
51
+ cabrillo_name = "ARI-DX"
52
+ mode = "BOTH" # CW SSB BOTH RTTY
53
+
54
+ columns = [
55
+ "YYYY-MM-DD HH:MM:SS",
56
+ "Call",
57
+ "Freq",
58
+ "Mode",
59
+ "Snt",
60
+ "Rcv",
61
+ "SentNr",
62
+ "RcvNr",
63
+ "PTS",
64
+ ]
65
+
66
+ advance_on_space = [True, True, True, True, True]
67
+
68
+ # 1 once per contest, 2 work each band, 3 each band/mode, 4 no dupe checking
69
+ dupe_type = 3
70
+
71
+
72
+ def init_contest(self):
73
+ """setup plugin"""
74
+ set_tab_next(self)
75
+ set_tab_prev(self)
76
+ interface(self)
77
+ self.next_field = self.other_2
78
+
79
+
80
+ def interface(self):
81
+ """Setup user interface"""
82
+ self.field1.show()
83
+ self.field2.show()
84
+ self.field3.show()
85
+ self.field4.show()
86
+ self.snt_label.setText("SNT")
87
+ self.field1.setAccessibleName("RST Sent")
88
+ self.exch_label.setText("Prov or SN")
89
+ self.field4.setAccessibleName("Province or Serial Number")
90
+
91
+
92
+ def reset_label(self): # pylint: disable=unused-argument
93
+ """reset label after field cleared"""
94
+
95
+
96
+ def set_tab_next(self):
97
+ """Set TAB Advances"""
98
+ self.tab_next = {
99
+ self.callsign: self.sent,
100
+ self.sent: self.receive,
101
+ self.receive: self.other_1,
102
+ self.other_1: self.other_2,
103
+ self.other_2: self.callsign,
104
+ }
105
+
106
+
107
+ def set_tab_prev(self):
108
+ """Set TAB Advances"""
109
+ self.tab_prev = {
110
+ self.callsign: self.other_2,
111
+ self.sent: self.callsign,
112
+ self.receive: self.sent,
113
+ self.other_1: self.receive,
114
+ self.other_2: self.other_1,
115
+ }
116
+
117
+
118
+ def set_contact_vars(self):
119
+ """Contest Specific"""
120
+ self.contact["SNT"] = self.sent.text()
121
+ self.contact["RCV"] = self.receive.text()
122
+ self.contact["NR"] = self.other_2.text().upper()
123
+ self.contact["SentNr"] = self.other_1.text()
124
+
125
+
126
+ def predupe(self): # pylint: disable=unused-argument
127
+ """called after callsign entered"""
128
+
129
+
130
+ def prefill(self):
131
+ """Fill sentnr"""
132
+ result = self.database.get_serial()
133
+ serial_nr = str(result.get("serial_nr", "1")).zfill(3)
134
+ if serial_nr == "None":
135
+ serial_nr = "001"
136
+
137
+ exchange = self.contest_settings.get("SentExchange", "").replace("#", serial_nr)
138
+ if len(self.other_1.text()) == 0:
139
+ self.other_1.setText(exchange)
140
+
141
+
142
+ def points(self) -> int:
143
+ """Calc point"""
144
+
145
+ # QSO Points: 0 points per QSO with same country
146
+ # 1 point per QSO with different country same continent
147
+ # 3 points per QSO with different continent
148
+ # 10 points per QSO with I/IS0/IT9 stations
149
+
150
+ if self.contact_is_dupe > 0:
151
+ return 0
152
+
153
+ result = self.cty_lookup(self.station.get("Call", ""))
154
+ if result:
155
+ for item in result.items():
156
+ mycountry = item[1].get("primary_pfx", "")
157
+ myentity = item[1].get("entity", "")
158
+ mycontinent = item[1].get("continent", "")
159
+
160
+ result = self.cty_lookup(self.contact.get("Call", ""))
161
+
162
+ if result:
163
+ for item in result.items():
164
+ hiscountry = item[1].get("primary_pfx", "")
165
+ hisentity = item[1].get("entity", "")
166
+ hiscontinent = item[1].get("continent", "")
167
+
168
+ _points = 0
169
+
170
+ if mycountry == hiscountry:
171
+ _points = 0
172
+ if mycountry != hiscountry and mycontinent == hiscontinent:
173
+ _points = 1
174
+ if mycontinent != hiscontinent:
175
+ _points = 3
176
+ if hiscountry in ("I", "IS0", "IT9"):
177
+ _points = 10
178
+
179
+ return _points
180
+
181
+
182
+ def show_mults(self):
183
+ """Return display string for mults"""
184
+
185
+ # Multipliers: Each Italian province once per band
186
+ # Each DXCC country once per band
187
+
188
+ _country = 0
189
+ _province = 0
190
+
191
+ sql = (
192
+ "select count(DISTINCT(NR || ':' || Band)) as mult_count from dxlog "
193
+ f"where ContestNR = {self.database.current_contest} and typeof(NR) = 'text';"
194
+ )
195
+ result = self.database.exec_sql(sql)
196
+ if result:
197
+ _province = result.get("mult_count", 0)
198
+
199
+ sql = (
200
+ "select count(DISTINCT(CountryPrefix || ':' || Band)) as cb_count from dxlog "
201
+ f"where ContestNR = {self.database.current_contest} and CountryPrefix NOT in ('I', 'IS');"
202
+ )
203
+ result2 = self.database.exec_sql(sql)
204
+ if result2:
205
+ _country = int(result2.get("cb_count", 0))
206
+
207
+ return _country + _province
208
+
209
+
210
+ def show_qso(self):
211
+ """Return qso count"""
212
+ result = self.database.fetch_qso_count()
213
+ if result:
214
+ return int(result.get("qsos", 0))
215
+ return 0
216
+
217
+
218
+ def calc_score(self):
219
+ """Return calculated score"""
220
+ result = self.database.fetch_points()
221
+ if result is not None:
222
+ score = result.get("Points", "0")
223
+ if score is None:
224
+ score = "0"
225
+ contest_points = int(score)
226
+ mults = int(show_mults(self))
227
+ return contest_points * mults
228
+ return 0
229
+
230
+
231
+ def adif(self):
232
+ """Call the generate ADIF function"""
233
+ gen_adif(self, cabrillo_name, cabrillo_name)
234
+
235
+
236
+ def output_cabrillo_line(line_to_output, ending, file_descriptor, file_encoding):
237
+ """"""
238
+ print(
239
+ line_to_output.encode(file_encoding, errors="ignore").decode(),
240
+ end=ending,
241
+ file=file_descriptor,
242
+ )
243
+
244
+
245
+ def cabrillo(self, file_encoding):
246
+ """Generates Cabrillo file. Maybe."""
247
+ # https://www.cw160.com/cabrillo.htm
248
+ logger.debug("******Cabrillo*****")
249
+ logger.debug("Station: %s", f"{self.station}")
250
+ logger.debug("Contest: %s", f"{self.contest_settings}")
251
+ now = datetime.datetime.now()
252
+ date_time = now.strftime("%Y-%m-%d_%H-%M-%S")
253
+ filename = (
254
+ str(Path.home())
255
+ + "/"
256
+ + f"{self.station.get('Call', '').upper()}_{cabrillo_name}_{date_time}.log"
257
+ )
258
+ logger.debug("%s", filename)
259
+ log = self.database.fetch_all_contacts_asc()
260
+ try:
261
+ with open(filename, "w", encoding=file_encoding, newline="") as file_descriptor:
262
+ output_cabrillo_line(
263
+ "START-OF-LOG: 3.0",
264
+ "\r\n",
265
+ file_descriptor,
266
+ file_encoding,
267
+ )
268
+ output_cabrillo_line(
269
+ f"CREATED-BY: Not1MM v{__version__}",
270
+ "\r\n",
271
+ file_descriptor,
272
+ file_encoding,
273
+ )
274
+ output_cabrillo_line(
275
+ f"CONTEST: {cabrillo_name}",
276
+ "\r\n",
277
+ file_descriptor,
278
+ file_encoding,
279
+ )
280
+ if self.station.get("Club", ""):
281
+ output_cabrillo_line(
282
+ f"CLUB: {self.station.get('Club', '').upper()}",
283
+ "\r\n",
284
+ file_descriptor,
285
+ file_encoding,
286
+ )
287
+ output_cabrillo_line(
288
+ f"CALLSIGN: {self.station.get('Call','')}",
289
+ "\r\n",
290
+ file_descriptor,
291
+ file_encoding,
292
+ )
293
+ output_cabrillo_line(
294
+ f"LOCATION: {self.station.get('ARRLSection', '')}",
295
+ "\r\n",
296
+ file_descriptor,
297
+ file_encoding,
298
+ )
299
+ output_cabrillo_line(
300
+ f"CATEGORY-OPERATOR: {self.contest_settings.get('OperatorCategory','')}",
301
+ "\r\n",
302
+ file_descriptor,
303
+ file_encoding,
304
+ )
305
+ output_cabrillo_line(
306
+ f"CATEGORY-ASSISTED: {self.contest_settings.get('AssistedCategory','')}",
307
+ "\r\n",
308
+ file_descriptor,
309
+ file_encoding,
310
+ )
311
+ output_cabrillo_line(
312
+ f"CATEGORY-BAND: {self.contest_settings.get('BandCategory','')}",
313
+ "\r\n",
314
+ file_descriptor,
315
+ file_encoding,
316
+ )
317
+ mode = self.contest_settings.get("ModeCategory", "")
318
+ if mode in ["SSB+CW", "SSB+CW+DIGITAL"]:
319
+ mode = "MIXED"
320
+ output_cabrillo_line(
321
+ f"CATEGORY-MODE: {mode}",
322
+ "\r\n",
323
+ file_descriptor,
324
+ file_encoding,
325
+ )
326
+ output_cabrillo_line(
327
+ f"CATEGORY-TRANSMITTER: {self.contest_settings.get('TransmitterCategory','')}",
328
+ "\r\n",
329
+ file_descriptor,
330
+ file_encoding,
331
+ )
332
+ if self.contest_settings.get("OverlayCategory", "") != "N/A":
333
+ output_cabrillo_line(
334
+ f"CATEGORY-OVERLAY: {self.contest_settings.get('OverlayCategory','')}",
335
+ "\r\n",
336
+ file_descriptor,
337
+ file_encoding,
338
+ )
339
+ output_cabrillo_line(
340
+ f"GRID-LOCATOR: {self.station.get('GridSquare','')}",
341
+ "\r\n",
342
+ file_descriptor,
343
+ file_encoding,
344
+ )
345
+ output_cabrillo_line(
346
+ f"CATEGORY-POWER: {self.contest_settings.get('PowerCategory','')}",
347
+ "\r\n",
348
+ file_descriptor,
349
+ file_encoding,
350
+ )
351
+
352
+ output_cabrillo_line(
353
+ f"CLAIMED-SCORE: {calc_score(self)}",
354
+ "\r\n",
355
+ file_descriptor,
356
+ file_encoding,
357
+ )
358
+ ops = f"@{self.station.get('Call','')}"
359
+ list_of_ops = self.database.get_ops()
360
+ for op in list_of_ops:
361
+ ops += f", {op.get('Operator', '')}"
362
+ output_cabrillo_line(
363
+ f"OPERATORS: {ops}",
364
+ "\r\n",
365
+ file_descriptor,
366
+ file_encoding,
367
+ )
368
+ output_cabrillo_line(
369
+ f"NAME: {self.station.get('Name', '')}",
370
+ "\r\n",
371
+ file_descriptor,
372
+ file_encoding,
373
+ )
374
+ output_cabrillo_line(
375
+ f"ADDRESS: {self.station.get('Street1', '')}",
376
+ "\r\n",
377
+ file_descriptor,
378
+ file_encoding,
379
+ )
380
+ output_cabrillo_line(
381
+ f"ADDRESS-CITY: {self.station.get('City', '')}",
382
+ "\r\n",
383
+ file_descriptor,
384
+ file_encoding,
385
+ )
386
+ output_cabrillo_line(
387
+ f"ADDRESS-STATE-PROVINCE: {self.station.get('State', '')}",
388
+ "\r\n",
389
+ file_descriptor,
390
+ file_encoding,
391
+ )
392
+ output_cabrillo_line(
393
+ f"ADDRESS-POSTALCODE: {self.station.get('Zip', '')}",
394
+ "\r\n",
395
+ file_descriptor,
396
+ file_encoding,
397
+ )
398
+ output_cabrillo_line(
399
+ f"ADDRESS-COUNTRY: {self.station.get('Country', '')}",
400
+ "\r\n",
401
+ file_descriptor,
402
+ file_encoding,
403
+ )
404
+ output_cabrillo_line(
405
+ f"EMAIL: {self.station.get('Email', '')}",
406
+ "\r\n",
407
+ file_descriptor,
408
+ file_encoding,
409
+ )
410
+ for contact in log:
411
+ the_date_and_time = contact.get("TS", "")
412
+ themode = contact.get("Mode", "")
413
+ if themode == "LSB" or themode == "USB":
414
+ themode = "PH"
415
+ if themode == "RTTY":
416
+ themode = "RY"
417
+ frequency = str(int(contact.get("Freq", "0"))).rjust(5)
418
+
419
+ loggeddate = the_date_and_time[:10]
420
+ loggedtime = the_date_and_time[11:13] + the_date_and_time[14:16]
421
+ output_cabrillo_line(
422
+ f"QSO: {frequency} {themode} {loggeddate} {loggedtime} "
423
+ f"{contact.get('StationPrefix', '').ljust(13)} "
424
+ f"{str(contact.get('SNT', '')).ljust(3)} "
425
+ f"{str(contact.get('SentNr', '')).upper().ljust(6)} "
426
+ f"{contact.get('Call', '').ljust(13)} "
427
+ f"{str(contact.get('RCV', '')).ljust(3)} "
428
+ f"{str(contact.get('Exchange1', '')).upper().ljust(6)}",
429
+ "\r\n",
430
+ file_descriptor,
431
+ file_encoding,
432
+ )
433
+ output_cabrillo_line("END-OF-LOG:", "\r\n", file_descriptor, file_encoding)
434
+ self.show_message_box(f"Cabrillo saved to: {filename}")
435
+ except IOError as exception:
436
+ logger.critical("cabrillo: IO error: %s, writing to %s", exception, filename)
437
+ self.show_message_box(f"Error saving Cabrillo: {exception} {filename}")
438
+ return
439
+
440
+
441
+ def trigger_update(self):
442
+ """Triggers the log window to update."""
443
+ cmd = {}
444
+ cmd["cmd"] = "UPDATELOG"
445
+ if self.log_window:
446
+ self.log_window.msg_from_main(cmd)
447
+
448
+
449
+ def recalculate_mults(self):
450
+ """Recalculates multipliers after change in logged qso."""
451
+
452
+
453
+ def process_esm(self, new_focused_widget=None, with_enter=False):
454
+ """ESM State Machine"""
455
+
456
+ # self.pref["run_state"]
457
+
458
+ # -----===== Assigned F-Keys =====-----
459
+ # self.esm_dict["CQ"]
460
+ # self.esm_dict["EXCH"]
461
+ # self.esm_dict["QRZ"]
462
+ # self.esm_dict["AGN"]
463
+ # self.esm_dict["HISCALL"]
464
+ # self.esm_dict["MYCALL"]
465
+ # self.esm_dict["QSOB4"]
466
+
467
+ # ----==== text fields ====----
468
+ # self.callsign
469
+ # self.sent
470
+ # self.receive
471
+ # self.other_1
472
+ # self.other_2
473
+
474
+ if new_focused_widget is not None:
475
+ self.current_widget = self.inputs_dict.get(new_focused_widget)
476
+
477
+ # print(f"checking esm {self.current_widget=} {with_enter=} {self.pref.get("run_state")=}")
478
+
479
+ for a_button in [
480
+ self.esm_dict["CQ"],
481
+ self.esm_dict["EXCH"],
482
+ self.esm_dict["QRZ"],
483
+ self.esm_dict["AGN"],
484
+ self.esm_dict["HISCALL"],
485
+ self.esm_dict["MYCALL"],
486
+ self.esm_dict["QSOB4"],
487
+ ]:
488
+ if a_button is not None:
489
+ self.restore_button_color(a_button)
490
+
491
+ buttons_to_send = []
492
+
493
+ if self.pref.get("run_state"):
494
+ if self.current_widget == "callsign":
495
+ if len(self.callsign.text()) < 3:
496
+ self.make_button_green(self.esm_dict["CQ"])
497
+ buttons_to_send.append(self.esm_dict["CQ"])
498
+ elif len(self.callsign.text()) > 2:
499
+ self.make_button_green(self.esm_dict["HISCALL"])
500
+ self.make_button_green(self.esm_dict["EXCH"])
501
+ buttons_to_send.append(self.esm_dict["HISCALL"])
502
+ buttons_to_send.append(self.esm_dict["EXCH"])
503
+
504
+ elif self.current_widget in ["other_2"]:
505
+ if self.other_2.text() == "":
506
+ self.make_button_green(self.esm_dict["AGN"])
507
+ buttons_to_send.append(self.esm_dict["AGN"])
508
+ else:
509
+ self.make_button_green(self.esm_dict["QRZ"])
510
+ buttons_to_send.append(self.esm_dict["QRZ"])
511
+ buttons_to_send.append("LOGIT")
512
+
513
+ if with_enter is True and bool(len(buttons_to_send)):
514
+ for button in buttons_to_send:
515
+ if button:
516
+ if button == "LOGIT":
517
+ self.save_contact()
518
+ continue
519
+ self.process_function_key(button)
520
+ else:
521
+ if self.current_widget == "callsign":
522
+ if len(self.callsign.text()) > 2:
523
+ self.make_button_green(self.esm_dict["MYCALL"])
524
+ buttons_to_send.append(self.esm_dict["MYCALL"])
525
+
526
+ elif self.current_widget in ["other_2"]:
527
+ if self.other_2.text() == "":
528
+ self.make_button_green(self.esm_dict["AGN"])
529
+ buttons_to_send.append(self.esm_dict["AGN"])
530
+ else:
531
+ self.make_button_green(self.esm_dict["EXCH"])
532
+ buttons_to_send.append(self.esm_dict["EXCH"])
533
+ buttons_to_send.append("LOGIT")
534
+
535
+ if with_enter is True and bool(len(buttons_to_send)):
536
+ for button in buttons_to_send:
537
+ if button:
538
+ if button == "LOGIT":
539
+ self.save_contact()
540
+ continue
541
+ self.process_function_key(button)
542
+
543
+
544
+ def populate_history_info_line(self):
545
+ result = self.database.fetch_call_history(self.callsign.text())
546
+ if result:
547
+ self.history_info.setText(f"{result.get('Call', '')}, {result.get('Sect', '')}")
548
+ else:
549
+ self.history_info.setText("")
550
+
551
+
552
+ def check_call_history(self):
553
+ """"""
554
+ result = self.database.fetch_call_history(self.callsign.text())
555
+ if result:
556
+ self.history_info.setText(f"{result.get('UserText','')}")
557
+ if self.other_2.text() == "":
558
+ self.other_2.setText(f"{result.get('Sect', '')}")
@@ -174,7 +174,7 @@ def show_mults(self):
174
174
 
175
175
  sql = (
176
176
  "select count(DISTINCT(NR || ':' || Band || ':' || Mode)) as mult_count from dxlog "
177
- "where ContestNR = {self.database.current_contest} and typeof(NR) = 'text';"
177
+ f"where ContestNR = {self.database.current_contest} and typeof(NR) = 'text';"
178
178
  )
179
179
  result = self.database.exec_sql(sql)
180
180
  if result:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: not1mm
3
- Version: 25.5.25
3
+ Version: 25.5.26
4
4
  Summary: NOT1MM Logger
5
5
  Author-email: Michael Bridak <michael.bridak@gmail.com>
6
6
  License: GPL-3.0-or-later
@@ -259,6 +259,7 @@ generated, 'cause I'm lazy, list of those who've submitted PR's.
259
259
 
260
260
  ## Recent Changes
261
261
 
262
+ - [25-5-26] Add ARI DX contest, Fix Canada Day mults.
262
263
  - [25-5-25] Added {PREVNR} macro to resend last logged serial number.
263
264
  - Add Bandmap mode indicators for CW, FT*, SSB, Beacons.
264
265
  - Made tuning with the VFO knob smoother.
@@ -898,8 +899,26 @@ blue rectangle shows the receivers bandwidth if one is reported.
898
899
 
899
900
  ![Bandmap Window](https://github.com/mbridak/not1mm/raw/master/pic/VFO_and_bandwidth_markers.png)
900
901
 
901
- Clicked on spots now tune the radio and set the callsign field. Previously
902
- worked calls are displayed in red.
902
+ Clicking on a spots tunes the radio to the spot frequency and sets the callsign field.
903
+
904
+ Previously worked calls are displayed in Red.
905
+
906
+ Callsigns that were marked with CTRL-M to work later are displayed in a Yellow-ish color.
907
+
908
+ In between the spots call and time is now a little icon to visually tell you what kind of spot it is.
909
+
910
+ ![Bandmap Icons](https://github.com/mbridak/not1mm/raw/master/pic/bandmap_icons.png)
911
+
912
+ - ○ CW
913
+ - ⦿ FT*
914
+ - ⌾ RTTY
915
+ - 🗼 Beacons
916
+ - @ Everything else
917
+
918
+ Secondary Icons:
919
+
920
+ - [P] POTA
921
+ - [S] SOTA
903
922
 
904
923
  ### The Check Partial Window
905
924
 
@@ -927,7 +946,9 @@ This window contains QSO rates and counts.
927
946
 
928
947
  You can control the VFO on a remote rig by following the directions listed in
929
948
  the link below. It's a small hardware project with a BOM of under $20, and
930
- consisting of two parts.
949
+ consisting of two parts. The VFO knob is now detectable on MacOS. I've made the
950
+ operation of the knob smoother by having the knob ignore frequency updates from
951
+ the radio while it's in rotation.
931
952
 
932
953
  1. Making the [VFO](https://github.com/mbridak/not1mm/blob/master/usb_vfo_knob/vfo.md)...
933
954
  2. Then... `Window`>`VFO`
@@ -3,7 +3,7 @@ not1mm/__main__.py,sha256=FZZGObcnSJEzEFj7a2K_-DPJUivmDS2tV5boPVudSJs,169592
3
3
  not1mm/bandmap.py,sha256=0JmZ32UvkaPjXs2xTgowX1GLvZo5zHU_Zo9y_GL-On4,31139
4
4
  not1mm/checkwindow.py,sha256=zEHlw40j6Wr3rvKbCQf2lcezCoiZqaBqEvBjQU5aKW0,7630
5
5
  not1mm/fsutils.py,sha256=ukHKxKTeNKxKwqRaJjtzRShL4X5Xl0jRBbADyy3Ifp8,1701
6
- not1mm/logwindow.py,sha256=zpCCcyRLK40D2rcxnIJBSVVqPEmTHKCrkAcfEj5sZ54,42624
6
+ not1mm/logwindow.py,sha256=hYzpCQKkwgILFUtP5uxQXy6uU9zCB0R-aBDAUsSOPFI,42625
7
7
  not1mm/lookupservice.py,sha256=GkY_qHZfrW6XHf8upIoaG4hCFqm0fg6Ganu9ConGrIc,2628
8
8
  not1mm/radio.py,sha256=4Lysf9BY3vdtYCHwKfzO5WN7IGyh4_lKSVuQ6F4Z08g,5536
9
9
  not1mm/ratewindow.py,sha256=iBjqdOetIEX0wSwdGM89Ibt4gVlFdE-K8HQPnkVPVOg,6965
@@ -36,7 +36,7 @@ not1mm/data/k6gte.not1mm-64.png,sha256=6ku45Gq1g5ezh04F07osoKRtanb3e4kbx5XdIEh3N
36
36
  not1mm/data/logwindow.ui,sha256=vfkNdzJgFs3tTOBKLDavF2zVMvNHWOZ82fAErRi6pQY,1436
37
37
  not1mm/data/logwindowx.ui,sha256=9FzDJtLRpagvAWcDjFdB9NnvNZ4bVxdTNHy1Jit2ido,1610
38
38
  not1mm/data/main.ui,sha256=gEWmfXmqLM-DiujjnPdU-4buYJF9TQxS9gE0JlVv_4Y,65142
39
- not1mm/data/new_contest.ui,sha256=WKg2jy8jxHg2d3pZsBTcdSNObKcd0eVbO-OoW0u0uxA,25309
39
+ not1mm/data/new_contest.ui,sha256=uYEn8RU8kkGI2jUC7_gsH5HpVvJdG79cl_10OpvFCXw,25412
40
40
  not1mm/data/not1mm.html,sha256=c9-mfjMwDt4f5pySUruz2gREW33CQ2_rCddM2z5CZQo,23273
41
41
  not1mm/data/opon.ui,sha256=mC4OhoVIfR1H9IqHAKXliPMm8VOVmxSEadpsFQ7XnS4,2247
42
42
  not1mm/data/pickcontest.ui,sha256=Pbb_YEOzQfacyhIRkx-M3ZGugIIPL1KPztdwVv5c_q0,1696
@@ -120,7 +120,7 @@ not1mm/lib/plugin_common.py,sha256=D1OBjyLmX7zuSPqgTCmHwXzAKA12J_zTQItvyIem-4Y,1
120
120
  not1mm/lib/select_contest.py,sha256=WsptLuwkouIHeocJL3oZ6-eUfEnhpwdc-x7eMZ_TIVM,359
121
121
  not1mm/lib/settings.py,sha256=5xnsagH48qGeCDhfxPWW9yaXtv8wT13yoIVvYt8h_Qs,16023
122
122
  not1mm/lib/super_check_partial.py,sha256=hwT2NRwobu0PLDyw6ltmbmcAtGBD02CKGFbgGWjXMqA,2334
123
- not1mm/lib/version.py,sha256=gcUyTawJD4PIn_mBa947znnmgM9-mvfHXIEnCSo7gnM,49
123
+ not1mm/lib/version.py,sha256=cxRNTW7axUKEXgODc90wjsXYQxIl8QXaOMWrjWgJAus,49
124
124
  not1mm/lib/versiontest.py,sha256=8vDNptuBBunn-1IGkjNaquehqBYUJyjrPSF8Igmd4_Y,1286
125
125
  not1mm/plugins/10_10_fall_cw.py,sha256=oJh3JKqjOpnWElSlZpiQ631UnaOd8qra5s9bl_QoInk,14783
126
126
  not1mm/plugins/10_10_spring_cw.py,sha256=p7dSDtbFK0e6Xouw2V6swYn3VFVgHKyx4IfRWyBjMZY,14786
@@ -128,6 +128,7 @@ not1mm/plugins/10_10_summer_phone.py,sha256=NWWT5YTZN6CkPl5Jy4lCuTqHd1R_JodhsOLq
128
128
  not1mm/plugins/10_10_winter_phone.py,sha256=4xoWLbnE2di5XnUUlhsvTR__E0Z4kbhu-rcUitPMR0U,14795
129
129
  not1mm/plugins/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
130
130
  not1mm/plugins/ari_40_80.py,sha256=_CaUWEo0zJONXvxPqJKyqmStJezqxEsNxOnEdSLmxzY,15333
131
+ not1mm/plugins/ari_dx.py,sha256=H1eBKS6m-6ZH7l6wUTb6BjQnS8JTiwEv99xzSZnB0ac,18284
131
132
  not1mm/plugins/arrl_10m.py,sha256=9p7EX2vAXVilF8y6AYHG4fXczU6g9QuMA2Pvj64pSXE,18389
132
133
  not1mm/plugins/arrl_160m.py,sha256=rdWDhmPN0h9ADTlYaRLk-5j8KVye0rUEs1tyD-kfHV0,20300
133
134
  not1mm/plugins/arrl_dx_cw.py,sha256=Bx6_PBmTHKf4l52XwLFVeR2y6F134kXbGTfEXC_1agk,18890
@@ -139,7 +140,7 @@ not1mm/plugins/arrl_ss_phone.py,sha256=PzybC--tSB7_oNLbbN9xYkMnftH-z6qgcGZUp3-JG
139
140
  not1mm/plugins/arrl_vhf_jan.py,sha256=paYrF_o1EotBRmXn_x9_hEM16SWx7sLDzoSoXcYXcCY,20201
140
141
  not1mm/plugins/arrl_vhf_jun.py,sha256=WwQ-UGFekIZj26bIbq3sLacTYMmUzBYySHhPz2APm2M,19293
141
142
  not1mm/plugins/arrl_vhf_sep.py,sha256=kq5Rncru74G9B76VwfXMeTaF9AL8hq-1vw9ZMYmJmvM,19326
142
- not1mm/plugins/canada_day.py,sha256=89aGXm4S9PjKL9FYm3z7BMFRz--MugQQilwQnzLJvv0,15770
143
+ not1mm/plugins/canada_day.py,sha256=L8pBKcfZGUycBfRS3g9PfeavNIL8CTRAUYk-fVMXDoU,15771
143
144
  not1mm/plugins/cq_160_cw.py,sha256=bmHtxVcIWsqmgqpMCMso7DCBERtAr9fIKIjgymSKtms,18711
144
145
  not1mm/plugins/cq_160_ssb.py,sha256=9KKDmmapETEebm1azfEzUWk1NyW3TBnYtBlFIPZYHBs,18754
145
146
  not1mm/plugins/cq_wpx_cw.py,sha256=F5lLuXumrviNHnNEk377OKeEJCnv_j9vnjHEcV70gFc,18249
@@ -182,9 +183,9 @@ not1mm/plugins/ukeidx.py,sha256=0ABGW7_9Ui0Rgr8mkPBxOJokAIerM1a4-HWnl6VsnV8,1910
182
183
  not1mm/plugins/vhf_sprint.py,sha256=a9QFTpv8XUbZ_GLjdVCh7svykFa-gXOWwKFZ6MD3uQM,19289
183
184
  not1mm/plugins/weekly_rtty.py,sha256=C8Xs3Q5UgSYx-mFFar8BVARWtmqlyrbeC98Ubzb4UN8,20128
184
185
  not1mm/plugins/winter_field_day.py,sha256=hmAMgkdqIXtnCNyUp8J9Bb8liN8wj10wps6ROuG-Bok,15284
185
- not1mm-25.5.25.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
186
- not1mm-25.5.25.dist-info/METADATA,sha256=LMao9elh21fHypET3g4kblWcGFyjDg5z0A1LSOMY2J0,41254
187
- not1mm-25.5.25.dist-info/WHEEL,sha256=zaaOINJESkSfm_4HQVc5ssNzHCPXhJm0kEUakpsEHaU,91
188
- not1mm-25.5.25.dist-info/entry_points.txt,sha256=pMcZk_0dxFgLkcUkF0Q874ojpwOmF3OL6EKw9LgvocM,47
189
- not1mm-25.5.25.dist-info/top_level.txt,sha256=0YmTxEcDzQlzXub-lXASvoLpg_mt1c2thb5cVkDf5J4,7
190
- not1mm-25.5.25.dist-info/RECORD,,
186
+ not1mm-25.5.26.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
187
+ not1mm-25.5.26.dist-info/METADATA,sha256=ADt5-FyZpdmXoqQlQzxeDeb2hnYOaXNyhCgg1cA7YmQ,41888
188
+ not1mm-25.5.26.dist-info/WHEEL,sha256=zaaOINJESkSfm_4HQVc5ssNzHCPXhJm0kEUakpsEHaU,91
189
+ not1mm-25.5.26.dist-info/entry_points.txt,sha256=pMcZk_0dxFgLkcUkF0Q874ojpwOmF3OL6EKw9LgvocM,47
190
+ not1mm-25.5.26.dist-info/top_level.txt,sha256=0YmTxEcDzQlzXub-lXASvoLpg_mt1c2thb5cVkDf5J4,7
191
+ not1mm-25.5.26.dist-info/RECORD,,