Project

General

Profile

Feature #10298 » vx6.py

Dan Smith, 01/22/2023 12:25 AM

 
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
(5-5/6)