not1mm 25.4.8__py3-none-any.whl → 25.4.11.3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,597 @@
1
+ """10 10 fall cw plugin"""
2
+
3
+ # pylint: disable=invalid-name, unused-argument, unused-variable, c-extension-no-member, unused-import
4
+
5
+ import datetime
6
+ import logging
7
+ from pathlib import Path
8
+
9
+ from PyQt6 import QtWidgets
10
+
11
+ from not1mm.lib.plugin_common import gen_adif, get_points, online_score_xml
12
+ from not1mm.lib.version import __version__
13
+
14
+ logger = logging.getLogger(__name__)
15
+
16
+ EXCHANGE_HINT = "# or # And Dist"
17
+
18
+ ukei_pfx = [
19
+ "G",
20
+ "GD",
21
+ "GJ",
22
+ "GM",
23
+ "GI",
24
+ "GW",
25
+ "GU",
26
+ "EI",
27
+ ]
28
+
29
+ name = "UKEI-DX"
30
+ cabrillo_name = "UKEI-DX"
31
+ mode = "BOTH" # CW SSB BOTH RTTY
32
+ # columns = [0, 1, 2, 3, 4, 5, 6, 15]
33
+ columns = [
34
+ "YYYY-MM-DD HH:MM:SS",
35
+ "Call",
36
+ "Freq",
37
+ "Snt",
38
+ "Rcv",
39
+ "SentNr",
40
+ "RcvNr",
41
+ "PTS",
42
+ ]
43
+
44
+ # 1 once per contest, 2 work each band, 3 each band/mode, 4 no dupe checking
45
+ dupe_type = 2
46
+
47
+
48
+ def init_contest(self):
49
+ """setup plugin"""
50
+ set_tab_next(self)
51
+ set_tab_prev(self)
52
+ interface(self)
53
+ self.next_field = self.other_2
54
+
55
+
56
+ def interface(self):
57
+ """Setup user interface"""
58
+ self.field1.show()
59
+ self.field2.show()
60
+ self.field3.show()
61
+ self.field4.show()
62
+ self.snt_label.setText("SNT")
63
+ self.field1.setAccessibleName("RST Sent")
64
+ self.other_label.setText("Sent Nr")
65
+ self.field2.setAccessibleName("Sent Number")
66
+ self.exch_label.setText("# and Dist")
67
+ self.field4.setAccessibleName("Number and District")
68
+
69
+
70
+ def reset_label(self):
71
+ """reset label after field cleared"""
72
+
73
+
74
+ def set_tab_next(self):
75
+ """Set TAB Advances"""
76
+ self.tab_next = {
77
+ self.callsign: self.sent,
78
+ self.sent: self.receive,
79
+ self.receive: self.other_1,
80
+ self.other_1: self.other_2,
81
+ self.other_2: self.callsign,
82
+ }
83
+
84
+
85
+ def set_tab_prev(self):
86
+ """Set TAB Advances"""
87
+ self.tab_prev = {
88
+ self.callsign: self.other_2,
89
+ self.sent: self.callsign,
90
+ self.receive: self.sent,
91
+ self.other_1: self.receive,
92
+ self.other_2: self.other_1,
93
+ }
94
+
95
+
96
+ def validate(self):
97
+ """doc"""
98
+ return True
99
+
100
+
101
+ def set_contact_vars(self):
102
+ """Contest Specific"""
103
+ self.contact["SNT"] = self.sent.text()
104
+ self.contact["RCV"] = self.receive.text()
105
+ self.contact["NR"] = self.other_2.text().upper()
106
+ self.contact["SentNr"] = self.other_1.text()
107
+
108
+
109
+ def predupe(self):
110
+ """called after callsign entered"""
111
+
112
+
113
+ def prefill(self):
114
+ """Fill sentnr"""
115
+ exch = str(self.contest_settings.get("SentExchange", 0))
116
+ result = self.database.get_serial()
117
+ serial_nr = str(result.get("serial_nr", "1")).zfill(3)
118
+ if serial_nr == "None":
119
+ serial_nr = "001"
120
+ if len(self.other_1.text()) == 0:
121
+ self.other_1.setText(exch.replace("#", serial_nr))
122
+
123
+
124
+ def points(self):
125
+ """Calc point"""
126
+
127
+ # UK/EI stations contacting :
128
+ # UK/EI/Europe 80m, 40m - 4 points 20m, 15m, 10m - 2 points
129
+ # DX (Outside Europe) 80m, 40m - 8 points 20m, 15m, 10m - 4 points
130
+
131
+ # Note : For UK/EI stations only, all QSOs will score double points between the hours of 0100z and 0459z.
132
+
133
+ # European stations contacting :
134
+ # UK/EI 80m, 40m - 4 points 20m, 15m, 10m - 2 points
135
+ # Europe 80m, 40m - 2 points 20m, 15m, 10m - 1 points
136
+ # DX (Outside Europe) 80m, 40m - 4 points 20m, 15m, 10m - 2 points
137
+
138
+ # DX (Outside Europe) contacting :
139
+ # UK/EI 80m, 40m - 8 points 20m, 15m, 10m - 4 points
140
+ # Europe 80m, 40m - 4 points 20m, 15m, 10m - 2 points
141
+ # DX (Outside Europe) 80m, 40m - 2 points 20m, 15m, 10m - 1 points
142
+
143
+ # f"{primary_pfx}: {continent}/{entity} cq:{cq} itu:{itu}"
144
+
145
+ if self.contact_is_dupe > 0:
146
+ return 0
147
+
148
+ myprimary_pfx = ""
149
+ # mycountry = ""
150
+ mycontinent = ""
151
+ hisprimary_pfx = ""
152
+ # hiscountry = ""
153
+ hiscontinent = ""
154
+
155
+ result = self.cty_lookup(self.station.get("Call", ""))
156
+ if result:
157
+ for item in result.items():
158
+ myprimary_pfx = item[1].get("primary_pfx", "")
159
+ # mycountry = item[1].get("entity", "")
160
+ mycontinent = item[1].get("continent", "")
161
+
162
+ result = self.cty_lookup(self.contact.get("Call", ""))
163
+ if result:
164
+ for item in result.items():
165
+ hisprimary_pfx = item[1].get("primary_pfx", "")
166
+ # hiscountry = item[1].get("entity", "")
167
+ hiscontinent = item[1].get("continent", "")
168
+
169
+ st = 100
170
+ et = 459
171
+ zt = datetime.datetime.now(datetime.timezone.utc).isoformat(" ")[11:16]
172
+ ct = int(zt[0:2]) * 100 + int(zt[3:5])
173
+ double_window = st <= ct <= et
174
+
175
+ # UK/EI stations:
176
+ if myprimary_pfx in ukei_pfx:
177
+ if hiscontinent == "EU":
178
+ if self.contact.get("Band", 0) in ["3.5", "7"]:
179
+ return 4 + (4 * double_window)
180
+ return 2 + (2 * double_window)
181
+ if self.contact.get("Band", 0) in ["3.5", "7"]:
182
+ return 8 + (8 * double_window)
183
+ return 4 + (4 * double_window)
184
+
185
+ # European stations:
186
+ if mycontinent == "EU":
187
+ if hisprimary_pfx in ukei_pfx:
188
+ if self.contact.get("Band", 0) in ["3.5", "7"]:
189
+ return 4
190
+ return 2
191
+ elif hiscontinent == "EU":
192
+ if self.contact.get("Band", 0) in ["3.5", "7"]:
193
+ return 2
194
+ return 1
195
+ if self.contact.get("Band", 0) in ["3.5", "7"]:
196
+ return 4
197
+ return 2
198
+
199
+ # DX (Outside Europe)
200
+ if mycontinent != "EU":
201
+ if hisprimary_pfx in ukei_pfx:
202
+ if self.contact.get("Band", "") in ["3.5", "7"]:
203
+ return 8
204
+ return 4
205
+ elif hiscontinent == "EU":
206
+ if self.contact.get("Band", "") in ["3.5", "7"]:
207
+ return 4
208
+ return 2
209
+ if self.contact.get("Band", "") in ["3.5", "7"]:
210
+ return 2
211
+ return 1
212
+
213
+ return 0
214
+
215
+
216
+ def show_mults(self):
217
+ """Return display string for mults"""
218
+
219
+ query = f"SELECT COUNT(DISTINCT CountryPrefix) as dxcc_count FROM DXLOG WHERE CountryPrefix NOT IN ('EI', 'G', 'GD', 'GI', 'GJ', 'GM', 'GU', 'GW') and ContestNR = {self.pref.get('contest', '1')};"
220
+ result = self.database.exec_sql(query)
221
+ dxcc_count = result.get("dxcc_count", 0)
222
+
223
+ query = f"SELECT COUNT(DISTINCT SUBSTR(NR, LENGTH(NR) - 1)) as code_count FROM DXLOG WHERE ContestNR = {self.pref.get('contest', '1')} and typeof(NR) = 'text';"
224
+ result = self.database.exec_sql(query)
225
+ code_count = result.get("code_count", 0)
226
+
227
+ return dxcc_count + code_count
228
+
229
+
230
+ def show_qso(self):
231
+ """Return qso count"""
232
+ result = self.database.fetch_qso_count()
233
+ if result:
234
+ return int(result.get("qsos", 0))
235
+ return 0
236
+
237
+
238
+ def calc_score(self):
239
+ """Return calculated score"""
240
+ result = self.database.fetch_points()
241
+ if result is not None:
242
+ score = result.get("Points", "0")
243
+ if score is None:
244
+ score = "0"
245
+ contest_points = int(score)
246
+ mults = show_mults(self)
247
+ return contest_points * mults
248
+ return 0
249
+
250
+
251
+ def adif(self):
252
+ """Call the generate ADIF function"""
253
+ gen_adif(self, cabrillo_name)
254
+
255
+
256
+ def output_cabrillo_line(line_to_output, ending, file_descriptor, file_encoding):
257
+ """"""
258
+ print(
259
+ line_to_output.encode(file_encoding, errors="ignore").decode(),
260
+ end=ending,
261
+ file=file_descriptor,
262
+ )
263
+
264
+
265
+ def cabrillo(self, file_encoding):
266
+ """Generates Cabrillo file. Maybe."""
267
+ # https://www.cqwpx.com/cabrillo.htm
268
+ logger.debug("******Cabrillo*****")
269
+ logger.debug("Station: %s", f"{self.station}")
270
+ logger.debug("Contest: %s", f"{self.contest_settings}")
271
+ now = datetime.datetime.now()
272
+ date_time = now.strftime("%Y-%m-%d_%H-%M-%S")
273
+ filename = (
274
+ str(Path.home())
275
+ + "/"
276
+ + f"{self.station.get('Call', '').upper()}_{cabrillo_name}_{date_time}.log"
277
+ )
278
+ logger.debug("%s", filename)
279
+ log = self.database.fetch_all_contacts_asc()
280
+ try:
281
+ with open(filename, "w", encoding=file_encoding, newline="") as file_descriptor:
282
+ output_cabrillo_line(
283
+ "START-OF-LOG: 3.0",
284
+ "\r\n",
285
+ file_descriptor,
286
+ file_encoding,
287
+ )
288
+ output_cabrillo_line(
289
+ f"CREATED-BY: Not1MM v{__version__}",
290
+ "\r\n",
291
+ file_descriptor,
292
+ file_encoding,
293
+ )
294
+ output_cabrillo_line(
295
+ f"CONTEST: {cabrillo_name}",
296
+ "\r\n",
297
+ file_descriptor,
298
+ file_encoding,
299
+ )
300
+ if self.station.get("Club", ""):
301
+ output_cabrillo_line(
302
+ f"CLUB: {self.station.get('Club', '').upper()}",
303
+ "\r\n",
304
+ file_descriptor,
305
+ file_encoding,
306
+ )
307
+ output_cabrillo_line(
308
+ f"CALLSIGN: {self.station.get('Call','')}",
309
+ "\r\n",
310
+ file_descriptor,
311
+ file_encoding,
312
+ )
313
+ output_cabrillo_line(
314
+ f"LOCATION: {self.station.get('ARRLSection', '')}",
315
+ "\r\n",
316
+ file_descriptor,
317
+ file_encoding,
318
+ )
319
+ output_cabrillo_line(
320
+ f"CATEGORY-OPERATOR: {self.contest_settings.get('OperatorCategory','')}",
321
+ "\r\n",
322
+ file_descriptor,
323
+ file_encoding,
324
+ )
325
+ output_cabrillo_line(
326
+ f"CATEGORY-ASSISTED: {self.contest_settings.get('AssistedCategory','')}",
327
+ "\r\n",
328
+ file_descriptor,
329
+ file_encoding,
330
+ )
331
+ output_cabrillo_line(
332
+ f"CATEGORY-BAND: {self.contest_settings.get('BandCategory','')}",
333
+ "\r\n",
334
+ file_descriptor,
335
+ file_encoding,
336
+ )
337
+ mode = self.contest_settings.get("ModeCategory", "")
338
+ if mode in ["SSB+CW", "SSB+CW+DIGITAL"]:
339
+ mode = "MIXED"
340
+ output_cabrillo_line(
341
+ f"CATEGORY-MODE: {mode}",
342
+ "\r\n",
343
+ file_descriptor,
344
+ file_encoding,
345
+ )
346
+ output_cabrillo_line(
347
+ f"CATEGORY-TRANSMITTER: {self.contest_settings.get('TransmitterCategory','')}",
348
+ "\r\n",
349
+ file_descriptor,
350
+ file_encoding,
351
+ )
352
+ if self.contest_settings.get("OverlayCategory", "") != "N/A":
353
+ output_cabrillo_line(
354
+ f"CATEGORY-OVERLAY: {self.contest_settings.get('OverlayCategory','')}",
355
+ "\r\n",
356
+ file_descriptor,
357
+ file_encoding,
358
+ )
359
+ output_cabrillo_line(
360
+ f"GRID-LOCATOR: {self.station.get('GridSquare','')}",
361
+ "\r\n",
362
+ file_descriptor,
363
+ file_encoding,
364
+ )
365
+ output_cabrillo_line(
366
+ f"CATEGORY-POWER: {self.contest_settings.get('PowerCategory','')}",
367
+ "\r\n",
368
+ file_descriptor,
369
+ file_encoding,
370
+ )
371
+
372
+ output_cabrillo_line(
373
+ f"CLAIMED-SCORE: {calc_score(self)}",
374
+ "\r\n",
375
+ file_descriptor,
376
+ file_encoding,
377
+ )
378
+ ops = f"@{self.station.get('Call','')}"
379
+ list_of_ops = self.database.get_ops()
380
+ for op in list_of_ops:
381
+ ops += f", {op.get('Operator', '')}"
382
+ output_cabrillo_line(
383
+ f"OPERATORS: {ops}",
384
+ "\r\n",
385
+ file_descriptor,
386
+ file_encoding,
387
+ )
388
+ output_cabrillo_line(
389
+ f"NAME: {self.station.get('Name', '')}",
390
+ "\r\n",
391
+ file_descriptor,
392
+ file_encoding,
393
+ )
394
+ output_cabrillo_line(
395
+ f"ADDRESS: {self.station.get('Street1', '')}",
396
+ "\r\n",
397
+ file_descriptor,
398
+ file_encoding,
399
+ )
400
+ output_cabrillo_line(
401
+ f"ADDRESS-CITY: {self.station.get('City', '')}",
402
+ "\r\n",
403
+ file_descriptor,
404
+ file_encoding,
405
+ )
406
+ output_cabrillo_line(
407
+ f"ADDRESS-STATE-PROVINCE: {self.station.get('State', '')}",
408
+ "\r\n",
409
+ file_descriptor,
410
+ file_encoding,
411
+ )
412
+ output_cabrillo_line(
413
+ f"ADDRESS-POSTALCODE: {self.station.get('Zip', '')}",
414
+ "\r\n",
415
+ file_descriptor,
416
+ file_encoding,
417
+ )
418
+ output_cabrillo_line(
419
+ f"ADDRESS-COUNTRY: {self.station.get('Country', '')}",
420
+ "\r\n",
421
+ file_descriptor,
422
+ file_encoding,
423
+ )
424
+ output_cabrillo_line(
425
+ f"EMAIL: {self.station.get('Email', '')}",
426
+ "\r\n",
427
+ file_descriptor,
428
+ file_encoding,
429
+ )
430
+ for contact in log:
431
+ the_date_and_time = contact.get("TS", "")
432
+ themode = contact.get("Mode", "")
433
+ if themode == "LSB" or themode == "USB":
434
+ themode = "PH"
435
+ frequency = str(int(contact.get("Freq", "0"))).rjust(5)
436
+
437
+ loggeddate = the_date_and_time[:10]
438
+ loggedtime = the_date_and_time[11:13] + the_date_and_time[14:16]
439
+ sentnr = str(contact.get("SentNr", "")).upper().split()
440
+ if len(sentnr) == 2:
441
+ sentnr = sentnr[0].zfill(3) + " " + sentnr[1]
442
+ else:
443
+ sentnr = sentnr[0].zfill(3) + " --"
444
+
445
+ nr = str(contact.get("NR", "")).upper().split()
446
+ if len(nr) == 2:
447
+ nr = nr[0].zfill(3) + " " + nr[1]
448
+ else:
449
+ if nr[0][-2:].isalpha():
450
+ nr = nr[0][:-2].zfill(3) + " " + nr[0][-2:]
451
+ else:
452
+ nr = nr[0].zfill(3) + " --"
453
+
454
+ output_cabrillo_line(
455
+ f"QSO: {frequency} {themode} {loggeddate} {loggedtime} "
456
+ f"{contact.get('StationPrefix', '').ljust(13)} "
457
+ f"{str(contact.get('SNT', '')).ljust(3)} "
458
+ f"{sentnr} "
459
+ f"{contact.get('Call', '').ljust(13)} "
460
+ f"{str(contact.get('RCV', '')).ljust(3)} "
461
+ f"{nr}",
462
+ "\r\n",
463
+ file_descriptor,
464
+ file_encoding,
465
+ )
466
+ output_cabrillo_line("END-OF-LOG:", "\r\n", file_descriptor, file_encoding)
467
+ self.show_message_box(f"Cabrillo saved to: {filename}")
468
+ except IOError as exception:
469
+ logger.critical("cabrillo: IO error: %s, writing to %s", exception, filename)
470
+ self.show_message_box(f"Error saving Cabrillo: {exception} {filename}")
471
+ return
472
+
473
+
474
+ def recalculate_mults(self):
475
+ """Recalculates multipliers after change in logged qso."""
476
+
477
+
478
+ def process_esm(self, new_focused_widget=None, with_enter=False):
479
+ """ESM State Machine"""
480
+
481
+ # self.pref["run_state"]
482
+
483
+ # -----===== Assigned F-Keys =====-----
484
+ # self.esm_dict["CQ"]
485
+ # self.esm_dict["EXCH"]
486
+ # self.esm_dict["QRZ"]
487
+ # self.esm_dict["AGN"]
488
+ # self.esm_dict["HISCALL"]
489
+ # self.esm_dict["MYCALL"]
490
+ # self.esm_dict["QSOB4"]
491
+
492
+ # ----==== text fields ====----
493
+ # self.callsign
494
+ # self.sent
495
+ # self.receive
496
+ # self.other_1
497
+ # self.other_2
498
+
499
+ if new_focused_widget is not None:
500
+ self.current_widget = self.inputs_dict.get(new_focused_widget)
501
+
502
+ # print(f"checking esm {self.current_widget=} {with_enter=} {self.pref.get("run_state")=}")
503
+
504
+ for a_button in [
505
+ self.esm_dict["CQ"],
506
+ self.esm_dict["EXCH"],
507
+ self.esm_dict["QRZ"],
508
+ self.esm_dict["AGN"],
509
+ self.esm_dict["HISCALL"],
510
+ self.esm_dict["MYCALL"],
511
+ self.esm_dict["QSOB4"],
512
+ ]:
513
+ if a_button is not None:
514
+ self.restore_button_color(a_button)
515
+
516
+ buttons_to_send = []
517
+
518
+ if self.pref.get("run_state"):
519
+ if self.current_widget == "callsign":
520
+ if len(self.callsign.text()) < 3:
521
+ self.make_button_green(self.esm_dict["CQ"])
522
+ buttons_to_send.append(self.esm_dict["CQ"])
523
+ elif len(self.callsign.text()) > 2:
524
+ self.make_button_green(self.esm_dict["HISCALL"])
525
+ self.make_button_green(self.esm_dict["EXCH"])
526
+ buttons_to_send.append(self.esm_dict["HISCALL"])
527
+ buttons_to_send.append(self.esm_dict["EXCH"])
528
+
529
+ elif self.current_widget in ["other_2"]:
530
+ if self.other_2.text() == "":
531
+ self.make_button_green(self.esm_dict["AGN"])
532
+ buttons_to_send.append(self.esm_dict["AGN"])
533
+ else:
534
+ self.make_button_green(self.esm_dict["QRZ"])
535
+ buttons_to_send.append(self.esm_dict["QRZ"])
536
+ buttons_to_send.append("LOGIT")
537
+
538
+ if with_enter is True and bool(len(buttons_to_send)):
539
+ for button in buttons_to_send:
540
+ if button:
541
+ if button == "LOGIT":
542
+ self.save_contact()
543
+ continue
544
+ self.process_function_key(button)
545
+ else:
546
+ if self.current_widget == "callsign":
547
+ if len(self.callsign.text()) > 2:
548
+ self.make_button_green(self.esm_dict["MYCALL"])
549
+ buttons_to_send.append(self.esm_dict["MYCALL"])
550
+
551
+ elif self.current_widget in ["other_2"]:
552
+ if self.other_2.text() == "":
553
+ self.make_button_green(self.esm_dict["AGN"])
554
+ buttons_to_send.append(self.esm_dict["AGN"])
555
+ else:
556
+ self.make_button_green(self.esm_dict["EXCH"])
557
+ buttons_to_send.append(self.esm_dict["EXCH"])
558
+ buttons_to_send.append("LOGIT")
559
+
560
+ if with_enter is True and bool(len(buttons_to_send)):
561
+ for button in buttons_to_send:
562
+ if button:
563
+ if button == "LOGIT":
564
+ self.save_contact()
565
+ continue
566
+ self.process_function_key(button)
567
+
568
+
569
+ def get_mults(self):
570
+ """"""
571
+
572
+ mults = {}
573
+ return mults
574
+
575
+
576
+ def just_points(self):
577
+ """"""
578
+ return get_points(self)
579
+
580
+
581
+ def populate_history_info_line(self):
582
+ result = self.database.fetch_call_history(self.callsign.text())
583
+ if result:
584
+ self.history_info.setText(
585
+ f"{result.get('Call', '')}, {result.get('Exch1', '')}, {result.get('UserText','...')}"
586
+ )
587
+ else:
588
+ self.history_info.setText("")
589
+
590
+
591
+ def check_call_history(self):
592
+ """"""
593
+ result = self.database.fetch_call_history(self.callsign.text())
594
+ if result:
595
+ self.history_info.setText(f"{result.get('UserText','')}")
596
+ if self.other_2.text() == "":
597
+ self.other_2.setText(f"{result.get('Exch1', '')}")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: not1mm
3
- Version: 25.4.8
3
+ Version: 25.4.11.3
4
4
  Summary: NOT1MM Logger
5
5
  Author-email: Michael Bridak <michael.bridak@gmail.com>
6
6
  License: GPL-3.0-or-later
@@ -141,6 +141,7 @@ Dynamic: license-file
141
141
  - [The exchange](#the-exchange)
142
142
  - [RAEM](#raem)
143
143
  - [RandomGram](#randomgram)
144
+ - [UKEI DX](#ukei-dx)
144
145
 
145
146
  ## What and Why is Not1MM
146
147
 
@@ -241,13 +242,22 @@ generated, 'cause I'm lazy, list of those who've submitted PR's.
241
242
  - RandomGram
242
243
  - RAC Canada Day
243
244
  - REF CW, SSB
245
+ - SAC CW, SSB
244
246
  - SPDX
245
247
  - Stew Perry Topband
248
+ - UK/EI DX
246
249
  - Weekly RTTY
247
250
  - Winter Field Day
248
251
 
249
252
  ## Recent Changes
250
253
 
254
+ - [25-4-11-3] Fixed issue with winkeyer not sending multiple macros in ESM mode.
255
+ - [25-4-11-2] Fixed a crash.
256
+ - [25-4-11-1] Add clear buffer to winkeyer interface to stop sending.
257
+ - [25-4-11] Add Scandinavian Activity Contest
258
+ - [25-4-10-1] Add ARI 40/80 contest. Add Auto CQ visual indicator. Add CTRL-R to toggle Run state.
259
+ - [25-4-10] Add Auto CQ visual indicator.
260
+ - [25-4-9] Added UKEI DX
251
261
  - [25-4-8] Remove focus from statistics table widget.
252
262
  - [25-4-7] Merge in changes from dj1yfk correcting SPDX Cabrillo name.
253
263
  - [25-4-5] Add SPDX.
@@ -661,9 +671,13 @@ pressing F1 - F12. See next section on Editing macro keys.
661
671
  ### Auto CQ
662
672
 
663
673
  If you press `SHIFT-F1` The Auto CQ mode will be activated and the F1 macro will be resent
664
- after each Auto CQ Delay interval has passed. The delay can be changed by going to the
665
- `Options` TAB in the Configuration dialog. If you are in S&P mode, you will be automatically
666
- switched into RUN mode.
674
+ after each Auto CQ Delay interval has passed. An indicator will appear to the upper left of
675
+ the F1 macro key as a visual reminder that your Auto CQ is active.
676
+
677
+ ![Auto CQ Visual Indicator](https://github.com/mbridak/not1mm/raw/master/pic/auto_cq_indicator.png)
678
+
679
+ The delay can be changed by going to the `Options` TAB in the Configuration dialog. If you are in
680
+ S&P mode when you enable Auto CQ, you will be automatically switched into RUN mode.
667
681
 
668
682
  The auto CQ can be cancelled by either typing in the call sign field, or by pressing ESC.
669
683
 
@@ -770,6 +784,7 @@ is this has happened, since the gridsquare will replace the word "Regional".
770
784
  | [SPACE] | When in the callsign field, will move the input to the first field needed for the exchange. |
771
785
  | [Enter] | Submits the fields to the log. Unless ESM is enabled. |
772
786
  | [F1-F12] | Send (CW/RTTY/Voice) macros. |
787
+ | [CTRL-R] | Toggle between Run and S&P modes. |
773
788
  | [CTRL-S] | Spot Callsign to the cluster. |
774
789
  | [CTRL-M] | Mark Callsign to the bandmap window to work later. |
775
790
  | [CTRL-G] | Tune to a spot matching partial text in the callsign entry field (CAT Required). |
@@ -1034,3 +1049,8 @@ for me 33N117W. And in the exchange macro put `# {EXCH}`.
1034
1049
  ### RandomGram
1035
1050
 
1036
1051
  This plugin was submitted by @alduhoo. It reads a rg.txt file if it exists in the user's home directory to populate the next group in the sent exchange field.
1052
+
1053
+ ### UKEI DX
1054
+
1055
+ For the Run exchange macro I'd put '{SNT} # {EXCH}'
1056
+