1
|
# Copyright 2010 Dan Smith <dsmith@danplanet.com>
|
2
|
#
|
3
|
# This program is free software: you can redistribute it and/or modify
|
4
|
# it under the terms of the GNU General Public License as published by
|
5
|
# the Free Software Foundation, either version 3 of the License, or
|
6
|
# (at your option) any later version.
|
7
|
#
|
8
|
# This program is distributed in the hope that it will be useful,
|
9
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
10
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
11
|
# GNU General Public License for more details.
|
12
|
#
|
13
|
# You should have received a copy of the GNU General Public License
|
14
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
15
|
|
16
|
from chirp.drivers import yaesu_clone
|
17
|
from chirp import chirp_common, directory, bitwise
|
18
|
from chirp.settings import RadioSettingGroup, RadioSetting, RadioSettings
|
19
|
from chirp.settings import RadioSettingValueString, RadioSettingValueList
|
20
|
from chirp.settings import RadioSettingValueBoolean
|
21
|
from textwrap import dedent
|
22
|
import re
|
23
|
|
24
|
# flags.{even|odd}_pskip: These are actually "preferential *scan* channels".
|
25
|
# Is that what they mean on other radios as well?
|
26
|
|
27
|
# memory {
|
28
|
# step_changed: Channel step has been changed. Bit stays on even after
|
29
|
# you switch back to default step. Don't know why you would
|
30
|
# care
|
31
|
# half_deviation: 2.5 kHz deviation
|
32
|
# cpu_shifted: CPU freq has been shifted (to move a birdie out of channel)
|
33
|
# power: 0-3: ["L1", "L2", "L3", "Hi"]
|
34
|
# pager: Set if this is a paging memory
|
35
|
# tmodes: 0-7: ["", "Tone", "TSQL", "DTCS", "Rv Tn", "D Code",
|
36
|
# "T DCS", "D Tone"]
|
37
|
# Rv Tn: Reverse CTCSS - mutes receiver on tone
|
38
|
# The final 3 are for split:
|
39
|
# D Code: DCS Encode only
|
40
|
# T DCS: Encodes tone, decodes DCS code
|
41
|
# D Tone: Encodes DCS code, decodes tone
|
42
|
# }
|
43
|
MEM_FORMAT = """
|
44
|
#seekto 0x010A;
|
45
|
struct {
|
46
|
u8 auto_power_off;
|
47
|
u8 arts_beep;
|
48
|
u8 bell;
|
49
|
u8 beep_level;
|
50
|
u8 arts_cwid_alpha[16];
|
51
|
u8 unk1[2];
|
52
|
u8 channel_counter_width;
|
53
|
u8 lcd_dimmer;
|
54
|
u8 last_dtmf;
|
55
|
u8 unk2;
|
56
|
u8 internet_code;
|
57
|
u8 last_internet_dtmf;
|
58
|
u8 unk3[3];
|
59
|
u8 emergency;
|
60
|
u8 unk4[16];
|
61
|
u8 lamp;
|
62
|
u8 lock;
|
63
|
u8 unk5;
|
64
|
u8 mic_gain;
|
65
|
u8 unk6[2];
|
66
|
u8 on_timer;
|
67
|
u8 open_message_mode;
|
68
|
u8 open_message[6];
|
69
|
u8 unk7;
|
70
|
u8 unk8:6,
|
71
|
pager_answer_back:1,
|
72
|
unk9:1;
|
73
|
u8 pager_rx_tone1;
|
74
|
u8 pager_rx_tone2;
|
75
|
u8 pager_tx_tone1;
|
76
|
u8 pager_tx_tone2;
|
77
|
u8 password[4];
|
78
|
u8 ptt_delay;
|
79
|
u8 rf_squelch;
|
80
|
u8 rx_save;
|
81
|
u8 resume;
|
82
|
u8 unk10[5];
|
83
|
u8 tx_timeout;
|
84
|
u8 wakeup;
|
85
|
u8 vfo_mode:1,
|
86
|
arts_cwid:1,
|
87
|
scan_lamp:1,
|
88
|
ts_speed:1,
|
89
|
unk11:1,
|
90
|
beep:1,
|
91
|
unk12:1,
|
92
|
dtmf_autodial:1;
|
93
|
u8 busy_led:1,
|
94
|
tone_search_mute:1,
|
95
|
int_autodial:1,
|
96
|
bclo:1,
|
97
|
edge_beep:1,
|
98
|
unk13:1,
|
99
|
dmr_wrt:1,
|
100
|
tx_saver:1;
|
101
|
u8 unk14:2,
|
102
|
smart_search:1,
|
103
|
unk15:3,
|
104
|
home_rev:1,
|
105
|
moni_tcall:1;
|
106
|
u8 unk16:3,
|
107
|
arts_interval:1,
|
108
|
unk17:3,
|
109
|
memory_method:1;
|
110
|
u8 unk18:2,
|
111
|
internet_mode:1,
|
112
|
wx_alert:1,
|
113
|
unk19:1,
|
114
|
att:1,
|
115
|
unk20:2;
|
116
|
} settings;
|
117
|
|
118
|
#seekto 0x018A;
|
119
|
struct {
|
120
|
u16 in_use;
|
121
|
} bank_used[24];
|
122
|
|
123
|
#seekto 0x01D8;
|
124
|
u8 clock_shift;
|
125
|
|
126
|
#seekto 0x0214;
|
127
|
u16 banksoff1;
|
128
|
|
129
|
#seekto 0x0248;
|
130
|
u8 lastsetting1;
|
131
|
|
132
|
#seekto 0x0294;
|
133
|
u16 banksoff2;
|
134
|
|
135
|
#seekto 0x0248;
|
136
|
u8 lastsetting2;
|
137
|
|
138
|
#seekto 0x02CA;
|
139
|
struct {
|
140
|
u8 memory[16];
|
141
|
} dtmf[10];
|
142
|
|
143
|
#seekto 0x03CA;
|
144
|
struct {
|
145
|
u8 memory[8];
|
146
|
u8 empty_ff[8];
|
147
|
} internet_dtmf[64];
|
148
|
|
149
|
#seekto 0x097A;
|
150
|
struct {
|
151
|
u8 name[6];
|
152
|
} bank_names[24];
|
153
|
|
154
|
#seekto 0x0C0A;
|
155
|
struct {
|
156
|
u16 channels[100];
|
157
|
} banks[24];
|
158
|
|
159
|
#seekto 0x1ECA;
|
160
|
struct {
|
161
|
u8 even_pskip:1,
|
162
|
even_skip:1,
|
163
|
even_valid:1,
|
164
|
even_masked:1,
|
165
|
odd_pskip:1,
|
166
|
odd_skip:1,
|
167
|
odd_valid:1,
|
168
|
odd_masked:1;
|
169
|
} flags[500];
|
170
|
|
171
|
#seekto 0x21CA;
|
172
|
struct {
|
173
|
u8 unknown11:1,
|
174
|
step_changed:1,
|
175
|
half_deviation:1,
|
176
|
cpu_shifted:1,
|
177
|
unknown12:4;
|
178
|
u8 mode:2,
|
179
|
duplex:2,
|
180
|
tune_step:4;
|
181
|
bbcd freq[3];
|
182
|
u8 power:2,
|
183
|
unknown2:2,
|
184
|
pager:1,
|
185
|
tmode:3;
|
186
|
u8 name[6];
|
187
|
bbcd offset[3];
|
188
|
u8 tone;
|
189
|
u8 dcs;
|
190
|
u8 unknown5;
|
191
|
} memory[999];
|
192
|
"""
|
193
|
|
194
|
DUPLEX = ["", "-", "+", "split"]
|
195
|
MODES = ["FM", "AM", "WFM", "FM"] # last is auto
|
196
|
TMODES = ["", "Tone", "TSQL",
|
197
|
"", # is this 1750?
|
198
|
"DTCS", "DTCS->", "Tone->DTCS", "DTCS->Tone"]
|
199
|
DTMFCHARSET = list("0123456789ABCD*#-")
|
200
|
STEPS = [5.0, 10.0, 12.5, 15.0, 20.0, 25.0, 50.0, 100.0,
|
201
|
9.0, 200.0, 5.0] # last is auto, 9.0k and 200.0k are unadvertised
|
202
|
|
203
|
CHARSET = ["%i" % int(x) for x in range(10)] + \
|
204
|
[chr(x) for x in range(ord("A"), ord("Z")+1)] + \
|
205
|
list(" +-/?[]u_" + ("\x00" * 9) + "$%%\x00*#.|=\\o@") + \
|
206
|
list("\x00" * 100)
|
207
|
|
208
|
PASS_CHARSET = list("0123456789ABCDEF")
|
209
|
|
210
|
POWER_LEVELS = [chirp_common.PowerLevel("Hi", watts=5.00),
|
211
|
chirp_common.PowerLevel("L3", watts=2.50),
|
212
|
chirp_common.PowerLevel("L2", watts=1.00),
|
213
|
chirp_common.PowerLevel("L1", watts=0.30)]
|
214
|
POWER_LEVELS_220 = [chirp_common.PowerLevel("Hi", watts=1.50),
|
215
|
chirp_common.PowerLevel("L3", watts=1.00),
|
216
|
chirp_common.PowerLevel("L2", watts=0.50),
|
217
|
chirp_common.PowerLevel("L1", watts=0.20)]
|
218
|
|
219
|
|
220
|
class VX6Bank(chirp_common.NamedBank):
|
221
|
"""A VX6 Bank"""
|
222
|
def get_name(self):
|
223
|
_bank = self._model._radio._memobj.bank_names[self.index]
|
224
|
name = ""
|
225
|
for i in _bank.name:
|
226
|
if i == 0xFF:
|
227
|
break
|
228
|
name += CHARSET[i & 0x7F]
|
229
|
return name.rstrip()
|
230
|
|
231
|
def set_name(self, name):
|
232
|
name = name.upper()
|
233
|
_bank = self._model._radio._memobj.bank_names[self.index]
|
234
|
_bank.name = [CHARSET.index(x) for x in name.ljust(6)[:6]]
|
235
|
|
236
|
|
237
|
class VX6BankModel(chirp_common.BankModel):
|
238
|
"""A VX-6 bank model"""
|
239
|
|
240
|
def get_num_mappings(self):
|
241
|
return len(self.get_mappings())
|
242
|
|
243
|
def get_mappings(self):
|
244
|
banks = self._radio._memobj.banks
|
245
|
bank_mappings = []
|
246
|
for index, _bank in enumerate(banks):
|
247
|
bank = VX6Bank(self, "%i" % index, "b%i" % (index + 1))
|
248
|
bank.index = index
|
249
|
bank_mappings.append(bank)
|
250
|
|
251
|
return bank_mappings
|
252
|
|
253
|
def _get_channel_numbers_in_bank(self, bank):
|
254
|
_bank_used = self._radio._memobj.bank_used[bank.index]
|
255
|
if _bank_used.in_use == 0xFFFF:
|
256
|
return set()
|
257
|
|
258
|
_members = self._radio._memobj.banks[bank.index]
|
259
|
return set([int(ch) + 1 for ch in _members.channels if ch != 0xFFFF])
|
260
|
|
261
|
def _update_bank_with_channel_numbers(self, bank, channels_in_bank):
|
262
|
_members = self._radio._memobj.banks[bank.index]
|
263
|
if len(channels_in_bank) > len(_members.channels):
|
264
|
raise Exception("Too many entries in bank %d" % bank.index)
|
265
|
|
266
|
empty = 0
|
267
|
for index, channel_number in enumerate(sorted(channels_in_bank)):
|
268
|
_members.channels[index] = channel_number - 1
|
269
|
empty = index + 1
|
270
|
for index in range(empty, len(_members.channels)):
|
271
|
_members.channels[index] = 0xFFFF
|
272
|
|
273
|
def add_memory_to_mapping(self, memory, bank):
|
274
|
channels_in_bank = self._get_channel_numbers_in_bank(bank)
|
275
|
channels_in_bank.add(memory.number)
|
276
|
self._update_bank_with_channel_numbers(bank, channels_in_bank)
|
277
|
_bank_used = self._radio._memobj.bank_used[bank.index]
|
278
|
_bank_used.in_use = 0x0000 # enable
|
279
|
|
280
|
# also needed for unit to recognize any banks?
|
281
|
self._radio._memobj.banksoff1 = 0x0000
|
282
|
self._radio._memobj.banksoff2 = 0x0000
|
283
|
# TODO: turn back off (0xFFFF) when all banks are empty?
|
284
|
|
285
|
def remove_memory_from_mapping(self, memory, bank):
|
286
|
channels_in_bank = self._get_channel_numbers_in_bank(bank)
|
287
|
try:
|
288
|
channels_in_bank.remove(memory.number)
|
289
|
except KeyError:
|
290
|
raise Exception("Memory %i is not in bank %s. Cannot remove" %
|
291
|
(memory.number, bank))
|
292
|
self._update_bank_with_channel_numbers(bank, channels_in_bank)
|
293
|
|
294
|
if not channels_in_bank:
|
295
|
_bank_used = self._radio._memobj.bank_used[bank.index]
|
296
|
_bank_used.in_use = 0xFFFF # disable bank
|
297
|
|
298
|
def get_mapping_memories(self, bank):
|
299
|
memories = []
|
300
|
for channel in self._get_channel_numbers_in_bank(bank):
|
301
|
memories.append(self._radio.get_memory(channel))
|
302
|
|
303
|
return memories
|
304
|
|
305
|
def get_memory_mappings(self, memory):
|
306
|
banks = []
|
307
|
for bank in self.get_mappings():
|
308
|
if memory.number in self._get_channel_numbers_in_bank(bank):
|
309
|
banks.append(bank)
|
310
|
|
311
|
return banks
|
312
|
|
313
|
|
314
|
@directory.register
|
315
|
class VX6Radio(yaesu_clone.YaesuCloneModeRadio):
|
316
|
"""Yaesu VX-6"""
|
317
|
BAUD_RATE = 19200
|
318
|
VENDOR = "Yaesu"
|
319
|
MODEL = "VX-6"
|
320
|
|
321
|
_model = b"AH021"
|
322
|
_memsize = 32587
|
323
|
_block_lengths = [10, 32577]
|
324
|
_block_size = 16
|
325
|
|
326
|
_APO = ("OFF", "30 min", "1 hour", "3 hour", "5 hour", "8 hour")
|
327
|
_ARTSBEEP = ("Off", "In Range", "Always")
|
328
|
_ARTS_INT = ("15 S", "25 S")
|
329
|
_BELL = ("OFF", "1", "3", "5", "8", "Continuous")
|
330
|
_BEEP_LEVEL = ["%i" % int(x) for x in range(1, 10)]
|
331
|
_CH_CNT = ("5 MHZ", "10 MHZ", "50 MHZ", "100 MHZ")
|
332
|
_DIM_LEVEL = ["%i" % int(x) for x in range(0, 13)]
|
333
|
_EMERGENCY = ("Beep", "Strobe", "Bp+Str", "Beam", "Bp+Bem", "CW",
|
334
|
"Bp+CW", "CWT")
|
335
|
_HOME_REV = ("HOME", "REV")
|
336
|
_INT_CD = ["%i" % int(x) for x in range(0, 10)] + \
|
337
|
[chr(x) for x in range(ord("A"), ord("F")+1)]
|
338
|
_INT_MD = ("SRG: Sister Radio Group", "FRG: Friendly Radio Group")
|
339
|
_LAMP = ("Key", "Continuous", "Off")
|
340
|
_LOCK = ("Key", "Dial", "Key+Dial", "PTT", "Key+PTT", "Dial+PTT", "All")
|
341
|
_MAN_AUTO = ("Manual", "Auto")
|
342
|
_MEM_W_MD = ("Lower", "Next")
|
343
|
_MONI_TCALL = ("MONI", "T-CALL")
|
344
|
_NUM_1_9 = ["%i" % int(x) for x in range(1, 10)]
|
345
|
_NUM_0_9 = ["%i" % int(x) for x in range(10)]
|
346
|
_NUM_0_63 = ["%i" % int(x) for x in range(64)]
|
347
|
_NUM_1_50 = ["%i" % int(x) for x in range(1, 51)]
|
348
|
_ON_TIMER = ["OFF"] + \
|
349
|
["%02d:%02d" % (t / 60, t % 60) for t in range(10, 1450, 10)]
|
350
|
_OPEN_MSG = ("Off", "DC Voltage", "Message")
|
351
|
_PTT_DELAY = ("OFF", "20MS", "50MS", "100MS", "200MS")
|
352
|
_RF_SQL = ("OFF", "S1", "S2", "S3", "S4", "S5",
|
353
|
"S6", "S7", "S8", "S9", "S9+")
|
354
|
_RX_SAVE = ("OFF", "200 ms", "300 MS", "500 MS", "1 S", "2 S")
|
355
|
_RESUME = ("3 SEC", "5 SEC", "10 SEC", "BUSY", "HOLD")
|
356
|
_SMART_SEARCH = ("SINGLE", "CONT")
|
357
|
_TOT = ("OFF", "1MIN", "3MIN", "5MIN", "10MIN")
|
358
|
_TS_SPEED = ("FAST", "SLOW")
|
359
|
_VFO_MODE = ("ALL", "BAND")
|
360
|
_WAKEUP = ("OFF", "5S", "10S", "20S", "30S", "EAI")
|
361
|
|
362
|
@classmethod
|
363
|
def get_prompts(cls):
|
364
|
rp = chirp_common.RadioPrompts()
|
365
|
rp.pre_download = _(dedent("""\
|
366
|
1. Turn radio off.
|
367
|
2. Connect cable to MIC/SP jack.
|
368
|
3. Press and hold in the [F/W] key while turning the radio on
|
369
|
("CLONE" will appear on the display).
|
370
|
4. <b>After clicking OK</b>, press the [BAND] key to send image."""))
|
371
|
rp.pre_upload = _(dedent("""\
|
372
|
1. Turn radio off.
|
373
|
2. Connect cable to MIC/SP jack.
|
374
|
3. Press and hold in the [F/W] key while turning the radio on
|
375
|
("CLONE" will appear on the display).
|
376
|
4. Press the [V/M] key ("-WAIT-" will appear on the LCD)."""))
|
377
|
return rp
|
378
|
|
379
|
def _checksums(self):
|
380
|
return [yaesu_clone.YaesuChecksum(0x0000, 0x7F49)]
|
381
|
|
382
|
def process_mmap(self):
|
383
|
self._memobj = bitwise.parse(MEM_FORMAT, self._mmap)
|
384
|
|
385
|
def get_features(self):
|
386
|
rf = chirp_common.RadioFeatures()
|
387
|
rf.has_bank = True
|
388
|
rf.has_bank_names = True
|
389
|
rf.has_dtcs_polarity = False
|
390
|
rf.valid_modes = ["FM", "WFM", "AM", "NFM"]
|
391
|
rf.valid_tmodes = ["", "Tone", "TSQL", "DTCS", "Cross"]
|
392
|
rf.valid_cross_modes = [x for x in TMODES if '-' in x]
|
393
|
rf.valid_duplexes = DUPLEX
|
394
|
rf.valid_tuning_steps = STEPS
|
395
|
rf.valid_power_levels = POWER_LEVELS
|
396
|
rf.memory_bounds = (1, 999)
|
397
|
rf.valid_bands = [(500000, 998990000)]
|
398
|
rf.valid_characters = "".join(CHARSET)
|
399
|
rf.valid_name_length = 6
|
400
|
rf.can_odd_split = True
|
401
|
rf.has_ctone = False
|
402
|
rf.has_settings = True
|
403
|
return rf
|
404
|
|
405
|
def get_raw_memory(self, number):
|
406
|
return repr(self._memobj.memory[number-1]) + \
|
407
|
repr(self._memobj.flags[(number-1)/2])
|
408
|
|
409
|
def get_memory(self, number):
|
410
|
_mem = self._memobj.memory[number-1]
|
411
|
_flg = self._memobj.flags[(number-1)/2]
|
412
|
|
413
|
nibble = ((number-1) % 2) and "even" or "odd"
|
414
|
used = _flg["%s_masked" % nibble]
|
415
|
valid = _flg["%s_valid" % nibble]
|
416
|
pskip = _flg["%s_pskip" % nibble]
|
417
|
skip = _flg["%s_skip" % nibble]
|
418
|
|
419
|
mem = chirp_common.Memory()
|
420
|
mem.number = number
|
421
|
|
422
|
if not used:
|
423
|
mem.empty = True
|
424
|
if not valid:
|
425
|
mem.empty = True
|
426
|
mem.power = POWER_LEVELS[0]
|
427
|
return mem
|
428
|
|
429
|
mem.freq = chirp_common.fix_rounded_step(int(_mem.freq) * 1000)
|
430
|
mem.offset = chirp_common.fix_rounded_step(int(_mem.offset) * 1000)
|
431
|
mem.rtone = mem.ctone = chirp_common.TONES[_mem.tone & 0x3f]
|
432
|
tmode = TMODES[_mem.tmode]
|
433
|
if '-' in tmode:
|
434
|
mem.tmode = 'Cross'
|
435
|
mem.cross_mode = tmode
|
436
|
else:
|
437
|
mem.tmode = tmode
|
438
|
mem.duplex = DUPLEX[_mem.duplex]
|
439
|
mem.mode = MODES[_mem.mode]
|
440
|
if mem.mode == "FM" and _mem.half_deviation:
|
441
|
mem.mode = "NFM"
|
442
|
mem.dtcs = chirp_common.DTCS_CODES[_mem.dcs & 0x7f]
|
443
|
mem.tuning_step = STEPS[_mem.tune_step]
|
444
|
mem.skip = pskip and "P" or skip and "S" or ""
|
445
|
|
446
|
if mem.freq > 220000000 and mem.freq < 225000000:
|
447
|
mem.power = POWER_LEVELS_220[3 - _mem.power]
|
448
|
else:
|
449
|
mem.power = POWER_LEVELS[3 - _mem.power]
|
450
|
|
451
|
for i in _mem.name:
|
452
|
if i == 0xFF:
|
453
|
break
|
454
|
mem.name += CHARSET[i & 0x7F]
|
455
|
mem.name = mem.name.rstrip()
|
456
|
|
457
|
return mem
|
458
|
|
459
|
def set_memory(self, mem):
|
460
|
_mem = self._memobj.memory[mem.number-1]
|
461
|
_flag = self._memobj.flags[(mem.number-1)/2]
|
462
|
|
463
|
nibble = ((mem.number-1) % 2) and "even" or "odd"
|
464
|
used = _flag["%s_masked" % nibble]
|
465
|
valid = _flag["%s_valid" % nibble]
|
466
|
|
467
|
# initialize new channel to safe defaults
|
468
|
if not mem.empty and not valid:
|
469
|
_flag["%s_valid" % nibble] = True
|
470
|
_mem.unknown11 = 0
|
471
|
_mem.step_changed = 0
|
472
|
_mem.cpu_shifted = 0
|
473
|
_mem.unknown12 = 0
|
474
|
_mem.unknown2 = 0
|
475
|
_mem.pager = 0
|
476
|
_mem.unknown5 = 0
|
477
|
|
478
|
if mem.empty and valid and not used:
|
479
|
_flag["%s_valid" % nibble] = False
|
480
|
return
|
481
|
_flag["%s_masked" % nibble] = not mem.empty
|
482
|
|
483
|
if mem.empty:
|
484
|
return
|
485
|
|
486
|
_mem.freq = mem.freq / 1000
|
487
|
_mem.offset = mem.offset / 1000
|
488
|
_mem.tone = chirp_common.TONES.index(mem.rtone)
|
489
|
if mem.tmode == 'Cross':
|
490
|
_mem.tmode = TMODES.index(mem.cross_mode)
|
491
|
else:
|
492
|
_mem.tmode = TMODES.index(mem.tmode)
|
493
|
_mem.duplex = DUPLEX.index(mem.duplex)
|
494
|
if mem.mode == "NFM":
|
495
|
_mem.mode = MODES.index("FM")
|
496
|
_mem.half_deviation = 1
|
497
|
else:
|
498
|
_mem.mode = MODES.index(mem.mode)
|
499
|
_mem.half_deviation = 0
|
500
|
_mem.dcs = chirp_common.DTCS_CODES.index(mem.dtcs)
|
501
|
_mem.tune_step = STEPS.index(mem.tuning_step)
|
502
|
if mem.power:
|
503
|
_mem.power = 3 - POWER_LEVELS.index(mem.power)
|
504
|
else:
|
505
|
_mem.power = 0
|
506
|
|
507
|
_flag["%s_pskip" % nibble] = mem.skip == "P"
|
508
|
_flag["%s_skip" % nibble] = mem.skip == "S"
|
509
|
|
510
|
_mem.name = [0xFF] * 6
|
511
|
for i in range(0, 6):
|
512
|
_mem.name[i] = CHARSET.index(mem.name.ljust(6)[i])
|
513
|
|
514
|
if mem.name.strip():
|
515
|
_mem.name[0] |= 0x80
|
516
|
|
517
|
def get_bank_model(self):
|
518
|
return VX6BankModel(self)
|
519
|
|
520
|
def _decode_chars(self, inarr):
|
521
|
outstr = ""
|
522
|
for i in inarr:
|
523
|
if i == 0xFF:
|
524
|
break
|
525
|
outchar = CHARSET[i & 0x7F]
|
526
|
if outchar != "\x00":
|
527
|
outstr += outchar
|
528
|
return outstr.rstrip()
|
529
|
|
530
|
def _encode_chars(self, instr, length=16):
|
531
|
outarr = []
|
532
|
instr = str(instr)
|
533
|
for i in range(length):
|
534
|
if i < len(instr):
|
535
|
outarr.append(CHARSET.index(instr[i]))
|
536
|
else:
|
537
|
outarr.append(0xFF)
|
538
|
return outarr
|
539
|
|
540
|
def _get_settings(self):
|
541
|
_settings = self._memobj.settings
|
542
|
basic = RadioSettingGroup("basic", "Basic")
|
543
|
arts = RadioSettingGroup("arts", "ARTS")
|
544
|
dtmf = RadioSettingGroup("dtmf", "DTMF")
|
545
|
wires = RadioSettingGroup("wires", "WIRES")
|
546
|
misc = RadioSettingGroup("misc", "Misc")
|
547
|
top = RadioSettings(basic, arts, dtmf, wires, misc)
|
548
|
|
549
|
# BASIC
|
550
|
|
551
|
val = RadioSettingValueList(
|
552
|
self._APO, self._APO[_settings.auto_power_off])
|
553
|
rs = RadioSetting("auto_power_off", "Auto Power Off", val)
|
554
|
basic.append(rs)
|
555
|
|
556
|
val = RadioSettingValueList(
|
557
|
self._BEEP_LEVEL, self._BEEP_LEVEL[_settings.beep_level])
|
558
|
rs = RadioSetting("beep_level", "Beep Level", val)
|
559
|
basic.append(rs)
|
560
|
|
561
|
val = RadioSettingValueList(
|
562
|
self._DIM_LEVEL, self._DIM_LEVEL[_settings.lcd_dimmer])
|
563
|
rs = RadioSetting("lcd_dimmer", "Dimmer Level", val)
|
564
|
basic.append(rs)
|
565
|
|
566
|
val = RadioSettingValueList(
|
567
|
self._LAMP, self._LAMP[_settings.lamp])
|
568
|
rs = RadioSetting("lamp", "Keypad Lamp", val)
|
569
|
basic.append(rs)
|
570
|
|
571
|
val = RadioSettingValueList(
|
572
|
self._LOCK, self._LOCK[_settings.lock])
|
573
|
rs = RadioSetting("lock", "Lock", val)
|
574
|
basic.append(rs)
|
575
|
|
576
|
val = RadioSettingValueList(
|
577
|
self._NUM_1_9, self._NUM_1_9[_settings.mic_gain])
|
578
|
rs = RadioSetting("mic_gain", "Mic Gain", val)
|
579
|
basic.append(rs)
|
580
|
|
581
|
val = RadioSettingValueList(
|
582
|
self._OPEN_MSG, self._OPEN_MSG[_settings.open_message_mode])
|
583
|
rs = RadioSetting("open_message_mode",
|
584
|
"Open Message Mode", val)
|
585
|
basic.append(rs)
|
586
|
|
587
|
val = RadioSettingValueString(0, 6,
|
588
|
self._decode_chars(
|
589
|
_settings.open_message))
|
590
|
val.set_charset(CHARSET)
|
591
|
rs = RadioSetting("open_message", "Opening Message", val)
|
592
|
basic.append(rs)
|
593
|
|
594
|
passstr = ""
|
595
|
for c in _settings.password:
|
596
|
if c < len(PASS_CHARSET):
|
597
|
passstr += PASS_CHARSET[c]
|
598
|
val = RadioSettingValueString(0, 4, passstr)
|
599
|
val.set_charset(PASS_CHARSET)
|
600
|
rs = RadioSetting("password", "Password", val)
|
601
|
basic.append(rs)
|
602
|
|
603
|
val = RadioSettingValueList(
|
604
|
self._RESUME, self._RESUME[_settings.resume])
|
605
|
rs = RadioSetting("resume", "Scan Resume", val)
|
606
|
basic.append(rs)
|
607
|
|
608
|
val = RadioSettingValueList(
|
609
|
self._MONI_TCALL, self._MONI_TCALL[_settings.moni_tcall])
|
610
|
rs = RadioSetting("moni_tcall", "MONI/T-CALL switch", val)
|
611
|
basic.append(rs)
|
612
|
|
613
|
rs = RadioSetting("scan_lamp", "Scan Lamp",
|
614
|
RadioSettingValueBoolean(_settings.scan_lamp))
|
615
|
basic.append(rs)
|
616
|
|
617
|
rs = RadioSetting("beep", "Keypad Beep",
|
618
|
RadioSettingValueBoolean(_settings.beep))
|
619
|
basic.append(rs)
|
620
|
|
621
|
rs = RadioSetting("busy_led", "Busy LED",
|
622
|
RadioSettingValueBoolean(_settings.busy_led))
|
623
|
basic.append(rs)
|
624
|
|
625
|
rs = RadioSetting("bclo", "Busy Channel Lock-Out",
|
626
|
RadioSettingValueBoolean(_settings.bclo))
|
627
|
basic.append(rs)
|
628
|
|
629
|
rs = RadioSetting("wx_alert", "WX Alert",
|
630
|
RadioSettingValueBoolean(_settings.wx_alert))
|
631
|
basic.append(rs)
|
632
|
|
633
|
rs = RadioSetting("att", "Attenuator",
|
634
|
RadioSettingValueBoolean(_settings.att))
|
635
|
basic.append(rs)
|
636
|
|
637
|
# ARTS
|
638
|
|
639
|
val = RadioSettingValueList(
|
640
|
self._ARTS_INT, self._ARTS_INT[_settings.arts_interval])
|
641
|
rs = RadioSetting("arts_interval", "ARTS Interval", val)
|
642
|
arts.append(rs)
|
643
|
|
644
|
val = RadioSettingValueList(
|
645
|
self._ARTSBEEP, self._ARTSBEEP[_settings.arts_beep])
|
646
|
rs = RadioSetting("arts_beep", "ARTS Beep", val)
|
647
|
arts.append(rs)
|
648
|
|
649
|
rs = RadioSetting("arts_cwid", "ARTS Send CWID",
|
650
|
RadioSettingValueBoolean(_settings.arts_cwid))
|
651
|
arts.append(rs)
|
652
|
|
653
|
val = RadioSettingValueString(0, 16,
|
654
|
self._decode_chars(
|
655
|
_settings.arts_cwid_alpha))
|
656
|
val.set_charset(CHARSET)
|
657
|
rs = RadioSetting("arts_cwid_alpha", "ARTS CW ID", val)
|
658
|
arts.append(rs)
|
659
|
|
660
|
# DTMF
|
661
|
|
662
|
val = RadioSettingValueList(
|
663
|
self._MAN_AUTO, self._MAN_AUTO[_settings.dtmf_autodial])
|
664
|
rs = RadioSetting("dtmf_autodial", "DTMF Autodial", val)
|
665
|
dtmf.append(rs)
|
666
|
|
667
|
val = RadioSettingValueList(
|
668
|
self._NUM_0_9, self._NUM_0_9[_settings.last_dtmf])
|
669
|
rs = RadioSetting("last_dtmf", "Last DTMF Memory Set", val)
|
670
|
dtmf.append(rs)
|
671
|
|
672
|
for i in range(10):
|
673
|
name = "dtmf_" + str(i)
|
674
|
dtmfsetting = self._memobj.dtmf[i]
|
675
|
dtmfstr = ""
|
676
|
for c in dtmfsetting.memory:
|
677
|
if c < len(DTMFCHARSET):
|
678
|
dtmfstr += DTMFCHARSET[c]
|
679
|
dtmfentry = RadioSettingValueString(0, 16, dtmfstr)
|
680
|
rs = RadioSetting(name, name.upper(), dtmfentry)
|
681
|
dtmf.append(rs)
|
682
|
|
683
|
# WIRES
|
684
|
|
685
|
val = RadioSettingValueList(
|
686
|
self._INT_CD, self._INT_CD[_settings.internet_code])
|
687
|
rs = RadioSetting("internet_code", "Internet Code", val)
|
688
|
wires.append(rs)
|
689
|
|
690
|
val = RadioSettingValueList(
|
691
|
self._INT_MD, self._INT_MD[_settings.internet_mode])
|
692
|
rs = RadioSetting("internet_mode",
|
693
|
"Internet Link Connection mode", val)
|
694
|
wires.append(rs)
|
695
|
|
696
|
val = RadioSettingValueList(
|
697
|
self._MAN_AUTO, self._MAN_AUTO[_settings.int_autodial])
|
698
|
rs = RadioSetting("int_autodial", "Internet Autodial", val)
|
699
|
wires.append(rs)
|
700
|
|
701
|
val = RadioSettingValueList(
|
702
|
self._NUM_0_63, self._NUM_0_63[_settings.last_internet_dtmf])
|
703
|
rs = RadioSetting("last_internet_dtmf",
|
704
|
"Last Internet DTMF Memory Set", val)
|
705
|
wires.append(rs)
|
706
|
|
707
|
for i in range(64):
|
708
|
name = "wires_dtmf_" + str(i)
|
709
|
dtmfsetting = self._memobj.internet_dtmf[i]
|
710
|
dtmfstr = ""
|
711
|
for c in dtmfsetting.memory:
|
712
|
if c < len(DTMFCHARSET):
|
713
|
dtmfstr += DTMFCHARSET[c]
|
714
|
dtmfentry = RadioSettingValueString(0, 8, dtmfstr)
|
715
|
rs = RadioSetting(name, name.upper(), dtmfentry)
|
716
|
wires.append(rs)
|
717
|
|
718
|
# MISC
|
719
|
|
720
|
val = RadioSettingValueList(
|
721
|
self._BELL, self._BELL[_settings.bell])
|
722
|
rs = RadioSetting("bell", "CTCSS/DCS Bell", val)
|
723
|
misc.append(rs)
|
724
|
|
725
|
val = RadioSettingValueList(
|
726
|
self._CH_CNT, self._CH_CNT[_settings.channel_counter_width])
|
727
|
rs = RadioSetting("channel_counter_width",
|
728
|
"Channel Counter Search Width", val)
|
729
|
misc.append(rs)
|
730
|
|
731
|
val = RadioSettingValueList(
|
732
|
self._EMERGENCY, self._EMERGENCY[_settings.emergency])
|
733
|
rs = RadioSetting("emergency", "Emergency alarm", val)
|
734
|
misc.append(rs)
|
735
|
|
736
|
val = RadioSettingValueList(
|
737
|
self._ON_TIMER, self._ON_TIMER[_settings.on_timer])
|
738
|
rs = RadioSetting("on_timer", "On Timer", val)
|
739
|
misc.append(rs)
|
740
|
|
741
|
rs = RadioSetting("pager_answer_back", "Pager Answer Back",
|
742
|
RadioSettingValueBoolean(
|
743
|
_settings.pager_answer_back))
|
744
|
misc.append(rs)
|
745
|
|
746
|
val = RadioSettingValueList(
|
747
|
self._NUM_1_50, self._NUM_1_50[_settings.pager_rx_tone1])
|
748
|
rs = RadioSetting("pager_rx_tone1", "Pager RX Tone 1", val)
|
749
|
misc.append(rs)
|
750
|
|
751
|
val = RadioSettingValueList(
|
752
|
self._NUM_1_50, self._NUM_1_50[_settings.pager_rx_tone2])
|
753
|
rs = RadioSetting("pager_rx_tone2", "Pager RX Tone 2", val)
|
754
|
misc.append(rs)
|
755
|
|
756
|
val = RadioSettingValueList(
|
757
|
self._NUM_1_50, self._NUM_1_50[_settings.pager_tx_tone1])
|
758
|
rs = RadioSetting("pager_tx_tone1", "Pager TX Tone 1", val)
|
759
|
misc.append(rs)
|
760
|
|
761
|
val = RadioSettingValueList(
|
762
|
self._NUM_1_50, self._NUM_1_50[_settings.pager_tx_tone2])
|
763
|
rs = RadioSetting("pager_tx_tone2", "Pager TX Tone 2", val)
|
764
|
misc.append(rs)
|
765
|
|
766
|
val = RadioSettingValueList(
|
767
|
self._PTT_DELAY, self._PTT_DELAY[_settings.ptt_delay])
|
768
|
rs = RadioSetting("ptt_delay", "PTT Delay", val)
|
769
|
misc.append(rs)
|
770
|
|
771
|
val = RadioSettingValueList(
|
772
|
self._RF_SQL, self._RF_SQL[_settings.rf_squelch])
|
773
|
rs = RadioSetting("rf_squelch", "RF Squelch", val)
|
774
|
misc.append(rs)
|
775
|
|
776
|
val = RadioSettingValueList(
|
777
|
self._RX_SAVE, self._RX_SAVE[_settings.rx_save])
|
778
|
rs = RadioSetting("rx_save", "RX Save", val)
|
779
|
misc.append(rs)
|
780
|
|
781
|
val = RadioSettingValueList(
|
782
|
self._TOT, self._TOT[_settings.tx_timeout])
|
783
|
rs = RadioSetting("tx_timeout", "TOT", val)
|
784
|
misc.append(rs)
|
785
|
|
786
|
val = RadioSettingValueList(
|
787
|
self._WAKEUP, self._WAKEUP[_settings.wakeup])
|
788
|
rs = RadioSetting("wakeup", "Wakeup", val)
|
789
|
misc.append(rs)
|
790
|
|
791
|
rs = RadioSetting("edge_beep", "Band-Edge Beep",
|
792
|
RadioSettingValueBoolean(_settings.edge_beep))
|
793
|
misc.append(rs)
|
794
|
|
795
|
val = RadioSettingValueList(
|
796
|
self._VFO_MODE, self._VFO_MODE[_settings.vfo_mode])
|
797
|
rs = RadioSetting("vfo_mode", "VFO Band Edge Limiting", val)
|
798
|
misc.append(rs)
|
799
|
|
800
|
rs = RadioSetting("tone_search_mute", "Tone Search Mute",
|
801
|
RadioSettingValueBoolean(_settings.tone_search_mute))
|
802
|
misc.append(rs)
|
803
|
|
804
|
val = RadioSettingValueList(
|
805
|
self._TS_SPEED, self._TS_SPEED[_settings.ts_speed])
|
806
|
rs = RadioSetting("ts_speed", "Tone Search Speed", val)
|
807
|
misc.append(rs)
|
808
|
|
809
|
rs = RadioSetting("dmr_wrt", "Direct Memory Recall Overwrite",
|
810
|
RadioSettingValueBoolean(_settings.dmr_wrt))
|
811
|
misc.append(rs)
|
812
|
|
813
|
rs = RadioSetting("tx_saver", "TX Battery Saver",
|
814
|
RadioSettingValueBoolean(_settings.tx_saver))
|
815
|
misc.append(rs)
|
816
|
|
817
|
val = RadioSettingValueList(
|
818
|
self._SMART_SEARCH, self._SMART_SEARCH[_settings.smart_search])
|
819
|
rs = RadioSetting("smart_search", "Smart Search", val)
|
820
|
misc.append(rs)
|
821
|
|
822
|
val = RadioSettingValueList(
|
823
|
self._HOME_REV, self._HOME_REV[_settings.home_rev])
|
824
|
rs = RadioSetting("home_rev", "HM/RV(EMG)R/H key", val)
|
825
|
misc.append(rs)
|
826
|
|
827
|
val = RadioSettingValueList(
|
828
|
self._MEM_W_MD, self._MEM_W_MD[_settings.memory_method])
|
829
|
rs = RadioSetting("memory_method", "Memory Write Method", val)
|
830
|
misc.append(rs)
|
831
|
|
832
|
return top
|
833
|
|
834
|
def get_settings(self):
|
835
|
try:
|
836
|
return self._get_settings()
|
837
|
except:
|
838
|
import traceback
|
839
|
print(traceback.format_exc())
|
840
|
return None
|
841
|
|
842
|
def set_settings(self, uisettings):
|
843
|
for element in uisettings:
|
844
|
if not isinstance(element, RadioSetting):
|
845
|
self.set_settings(element)
|
846
|
continue
|
847
|
if not element.changed():
|
848
|
continue
|
849
|
try:
|
850
|
setting = element.get_name()
|
851
|
_settings = self._memobj.settings
|
852
|
if re.match('internet_dtmf_\d', setting):
|
853
|
# set dtmf fields
|
854
|
dtmfstr = str(element.value).strip()
|
855
|
newval = []
|
856
|
for i in range(0, 8):
|
857
|
if i < len(dtmfstr):
|
858
|
newval.append(DTMFCHARSET.index(dtmfstr[i]))
|
859
|
else:
|
860
|
newval.append(0xFF)
|
861
|
idx = int(setting[-1:])
|
862
|
_settings = self._memobj.internet_dtmf[idx]
|
863
|
_settings.memory = newval
|
864
|
continue
|
865
|
elif re.match('dtmf_\d', setting):
|
866
|
# set dtmf fields
|
867
|
dtmfstr = str(element.value).strip()
|
868
|
newval = []
|
869
|
for i in range(0, 16):
|
870
|
if i < len(dtmfstr):
|
871
|
newval.append(DTMFCHARSET.index(dtmfstr[i]))
|
872
|
else:
|
873
|
newval.append(0xFF)
|
874
|
idx = int(setting[-1:])
|
875
|
_settings = self._memobj.dtmf[idx]
|
876
|
_settings.memory = newval
|
877
|
continue
|
878
|
oldval = getattr(_settings, setting)
|
879
|
newval = element.value
|
880
|
if setting == "arts_cwid_alpha":
|
881
|
newval = self._encode_chars(newval)
|
882
|
elif setting == "open_message":
|
883
|
newval = self._encode_chars(newval, 6)
|
884
|
elif setting == "password":
|
885
|
newval = self._encode_chars(newval, 4)
|
886
|
setattr(_settings, setting, newval)
|
887
|
except Exception as e:
|
888
|
raise
|