Project

General

Profile

Bug #1187 » ft90.py

Jens Jensen, 12/14/2013 08:14 AM

 
1
# Copyright 2011 Dan Smith <dsmith@danplanet.com>
2
# Copyright 2013 Jens Jensen AF5MI <kd4tjx@yahoo.com>
3
#
4
# This program is free software: you can redistribute it and/or modify
5
# it under the terms of the GNU General Public License as published by
6
# the Free Software Foundation, either version 3 of the License, or
7
# (at your option) any later version.
8
#
9
# This program is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
# GNU General Public License for more details.
13
#
14
# You should have received a copy of the GNU General Public License
15
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
16

    
17
from chirp import chirp_common, bitwise, memmap, directory, errors, util, yaesu_clone
18
from chirp.settings import RadioSetting, RadioSettingGroup, \
19
    RadioSettingValueInteger, RadioSettingValueList, \
20
    RadioSettingValueBoolean, RadioSettingValueString
21
import time, os, traceback, string, re
22
from textwrap import dedent
23

    
24
if os.getenv("CHIRP_DEBUG"):
25
    CHIRP_DEBUG = True
26
else:
27
    CHIRP_DEBUG=False
28

    
29
CMD_ACK = chr(0x06)
30

    
31
FT90_STEPS = [5.0, 10.0, 12.5, 15.0, 20.0, 25.0, 50.0]
32
FT90_MODES = ["AM", "FM", "Auto"]
33
FT90_TMODES = ["", "Tone", "TSQL", "", "DTCS"] # idx 3 (Bell) not supported yet
34
FT90_TONES = list(chirp_common.TONES)
35
for tone in [ 165.5, 171.3, 177.3 ]:
36
    FT90_TONES.remove(tone)
37
FT90_POWER_LEVELS_VHF = [chirp_common.PowerLevel("Hi", watts=50),
38
                    chirp_common.PowerLevel("Mid1", watts=20),
39
                    chirp_common.PowerLevel("Mid2", watts=10),
40
                    chirp_common.PowerLevel("Low", watts=5)]
41

    
42
FT90_POWER_LEVELS_UHF = [chirp_common.PowerLevel("Hi", watts=35),
43
                    chirp_common.PowerLevel("Mid1", watts=20),
44
                    chirp_common.PowerLevel("Mid2", watts=10),
45
                    chirp_common.PowerLevel("Low", watts=5)]
46

    
47
FT90_DUPLEX = ["", "-", "+", "split"]
48
FT90_CWID_CHARS = list(string.digits) + list(string.uppercase) + list(" ")
49
FT90_DTMF_CHARS = list("0123456789ABCD*#")
50
FT90_SPECIAL = [ "vfo_vhf", "home_vhf", "vfo_uhf", "home_uhf", \
51
        "pms_1L", "pms_1U", "pms_2L", "pms_2U"]
52

    
53
@directory.register
54
class FT90Radio(yaesu_clone.YaesuCloneModeRadio):
55
    VENDOR = "Yaesu"
56
    MODEL = "FT-90"
57
    ID = "\x8E\xF6"
58

    
59
    _memsize = 4063
60
    # block 03 (200 Bytes long) repeats 18 times; channel memories
61
    _block_lengths = [ 2, 232, 24 ] + ([200] * 18 ) +  [205]
62

    
63
    mem_format = """
64
    u16 id;
65
	#seekto 0x22;	
66
	struct {
67
		u8	dtmf_active;
68
		u8	dtmf1_len;
69
		u8	dtmf2_len;
70
		u8	dtmf3_len;
71
		u8	dtmf4_len;
72
		u8	dtmf5_len;
73
		u8	dtmf6_len;
74
		u8	dtmf7_len;
75
		u8	dtmf8_len;
76
        u8  dtmf1[8];
77
        u8  dtmf2[8];
78
        u8  dtmf3[8];
79
        u8  dtmf4[8];
80
        u8  dtmf5[8];
81
        u8  dtmf6[8];
82
        u8  dtmf7[8];
83
        u8  dtmf8[8];
84
		char cwid[7];
85
		u8 	unk1;
86
        u8  scan1:2,
87
			beep:1,
88
            unk3:3,
89
            rfsqlvl:2;
90
        u8  unk4:2,
91
            scan2:1,
92
            cwid_en:1,
93
			txnarrow:1,
94
			dtmfspeed:1,
95
			pttlock:2;
96
		u8	dtmftxdelay:3,
97
			fancontrol:2,
98
			unk5:3;
99
		u8	dimmer:3,
100
			unk6:1,
101
			lcdcontrast:4;
102
        u8  dcsmode:2,
103
            unk16:2,
104
            tot:4;
105
        u8  unk14;
106
		u8	unk8:1,
107
			ars:1,
108
			lock:1,
109
			txpwrsave:1,
110
			apo:4;
111
        u8  unk15;
112
        u8  unk9:4,
113
            key_lt:4;
114
        u8  unk10:4,
115
            key_rt:4;
116
        u8  unk11:4,
117
            key_p1:4;
118
        u8  unk12:4,
119
            key_p2:4;
120
        u8  unk13:4,
121
            key_acc:4;
122
		
123
	} settings;
124
	
125
    struct mem_struct {
126
      u8 mode:2,
127
         isUhf1:1,
128
         unknown1:2,
129
         step:3;
130
      u8 artsmode:2,
131
         unknown2:1,
132
         isUhf2:1
133
         power:2,
134
         shift:2;
135
      u8 skip:1,
136
         showname:1,
137
         unknown3:1,
138
         isUhfHi:1,
139
         unknown4:1,
140
         tmode:3;
141
      u32 rxfreq;
142
      u32 txfreqoffset;
143
      u8 UseDefaultName:1,
144
         ars:1,
145
         tone:6;
146
      u8 packetmode:1,
147
         unknown5:1,
148
         dcstone:6;
149
      char name[7];
150
    };
151

    
152
	#seekto 0x86;
153
    struct mem_struct vfo_vhf;
154
    struct mem_struct home_vhf;
155
    struct mem_struct vfo_uhf;
156
    struct mem_struct home_uhf;
157

    
158
    #seekto 0xEB;
159
    u8 chan_enable[23];    
160
    
161
    #seekto 0x101;
162
    struct {
163
        u8  pms_2U_enable:1,
164
            pms_2L_enable:1,
165
            pms_1U_enable:1,
166
            pms_1L_enable:1,
167
            unknown6:4;
168
    } special_enables;
169
	
170
	#seekto 0x102;
171
    struct mem_struct memory[180];
172

    
173
	#seekto 0xf12;
174
	struct mem_struct pms_1L;
175
	struct mem_struct pms_1U;
176
	struct mem_struct pms_2L;	
177
	struct mem_struct pms_2U;
178
	
179
	#seekto 0x0F7B;
180
	struct  {
181
	    char	demomsg1[50];
182
	    char	demomsg2[50];
183
	} demomsg;
184
	
185
    """
186
    @classmethod
187
    def get_prompts(cls):
188
        rp = chirp_common.RadioPrompts()
189
        rp.pre_download = _(dedent("""\
190
            1. Turn radio off.
191
            2. Connect mic and hold [ACC] on mic while powering on.
192
                ("CLONE" will appear on the display)
193
            3. Replace mic with PC programming cable.
194
            4. <b>After clicking OK</b>, press the [SET] key to send image."""))
195
        rp.pre_upload = _(dedent("""\
196
            1. Turn radio off.
197
            2. Connect mic and hold [ACC] on mic while powering on.
198
                ("CLONE" will appear on the display)
199
            3. Replace mic with PC programming cable.
200
            4. Press the [DISP/SS] key
201
                ("R" will appear on the lower left of LCD)."""))
202
        return rp
203

    
204
    @classmethod
205
    def match_model(cls, filedata, filename):
206
        return len(filedata) == cls._memsize
207

    
208
    def get_features(self):
209
        rf = chirp_common.RadioFeatures()
210
        rf.has_settings = True
211
        rf.has_ctone = False
212
        rf.has_bank = False
213
        rf.has_dtcs_polarity = False
214
        rf.has_dtcs = True
215
        rf.valid_modes = FT90_MODES
216
        rf.valid_tmodes = FT90_TMODES
217
        rf.valid_duplexes = FT90_DUPLEX
218
        rf.valid_tuning_steps = FT90_STEPS
219
        rf.valid_power_levels = FT90_POWER_LEVELS_VHF
220
        rf.valid_name_length = 7
221
        rf.valid_characters = chirp_common.CHARSET_ASCII
222
        rf.valid_skips = ["", "S"]
223
        rf.valid_special_chans = FT90_SPECIAL
224
        rf.memory_bounds = (1, 180)
225
        rf.valid_bands = [(100000000, 230000000), 
226
            (300000000, 530000000), (810000000, 999975000)]
227

    
228
        return rf
229

    
230
    def _read(self, blocksize, blocknum):
231
        data = self.pipe.read(blocksize+2)
232
        
233
        # chew echo'd ack
234
        self.pipe.write(CMD_ACK)
235
        time.sleep(0.02)
236
        self.pipe.read(1) # chew echoed ACK from 1-wire serial
237
        
238
        if len(data) == blocksize+2 and data[0] == chr(blocknum):
239
            checksum = yaesu_clone.YaesuChecksum(1, blocksize)
240
            if checksum.get_existing(data) != checksum.get_calculated(data):
241
                raise Exception("Checksum Failed [%02X<>%02X] block %02X, data len: %i" %
242
                                    (checksum.get_existing(data),
243
                                    checksum.get_calculated(data), blocknum, len(data) ))
244
            data = data[1:blocksize+1] # Chew blocknum and checksum
245
            
246
        else:
247
            raise Exception("Unable to read blocknum %02X expected blocksize %i got %i." %
248
                                (blocknum, blocksize+2, len(data)))
249

    
250
        return data        
251
    
252
    def _clone_in(self):
253
        # Be very patient with the radio
254
        self.pipe.setTimeout(4)
255
        start = time.time()
256
    
257
        data = ""
258
        blocknum = 0
259
        status = chirp_common.Status()
260
        status.msg = "Cloning..."
261
        self.status_fn(status)
262
        status.max = len(self._block_lengths)
263
        for blocksize in self._block_lengths:
264
            data += self._read(blocksize, blocknum)
265
            blocknum += 1
266
            status.cur = blocknum
267
            self.status_fn(status)
268
                
269
        print "Clone completed in %i seconds, blocks read: %i" % (time.time() - start, blocknum)
270
    
271
        return memmap.MemoryMap(data)
272
    
273
    def _clone_out(self):
274
        looppredelay = 0.4
275
        looppostdelay = 1.9
276
        start = time.time()
277
    
278
        blocknum = 0
279
        pos = 0
280
        status = chirp_common.Status()
281
        status.msg = "Cloning to radio..."
282
        self.status_fn(status)
283
        status.max = len(self._block_lengths)
284

    
285
        for blocksize in self._block_lengths:
286
            checksum = yaesu_clone.YaesuChecksum(pos, pos+blocksize-1)
287
            blocknumbyte = chr(blocknum)
288
            payloadbytes = self.get_mmap()[pos:pos+blocksize]
289
            checksumbyte = chr(checksum.get_calculated(self.get_mmap()))
290
            if CHIRP_DEBUG:
291
                print "Block %i - will send from %i to %i byte " % \
292
                    (blocknum, pos, pos + blocksize)
293
                print util.hexprint(blocknumbyte)
294
                print util.hexprint(payloadbytes)
295
                print util.hexprint(checksumbyte)
296
            # send wrapped bytes
297
            time.sleep(looppredelay)
298
            self.pipe.write(blocknumbyte)
299
            self.pipe.write(payloadbytes)
300
            self.pipe.write(checksumbyte)
301
            tmp = self.pipe.read(blocksize+2)  #chew echo
302
            if CHIRP_DEBUG:
303
                print "bytes echoed: "
304
                print util.hexprint(tmp)
305
            # radio is slow to write/ack:
306
            time.sleep(looppostdelay) 
307
            buf = self.pipe.read(1)
308
            if CHIRP_DEBUG:
309
                print "ack recd:"
310
                print util.hexprint(buf)
311
            if buf != CMD_ACK:
312
                raise Exception("Radio did not ack block %i" % blocknum)
313
            pos += blocksize
314
            blocknum += 1
315
            status.cur = blocknum
316
            self.status_fn(status)
317
    
318
        print "Clone completed in %i seconds" % (time.time() - start)
319
    
320
    def sync_in(self):
321
        try:
322
            self._mmap = self._clone_in()
323
        except errors.RadioError:
324
            raise
325
        except Exception, e:
326
            trace = traceback.format_exc()
327
            raise errors.RadioError("Failed to communicate with radio: %s" % trace)
328
        self.process_mmap()
329

    
330
    def sync_out(self):
331
        try:
332
            self._clone_out()
333
        except errors.RadioError:
334
            raise
335
        except Exception, e:
336
            trace = traceback.format_exc()
337
            raise errors.RadioError("Failed to communicate with radio: %s" % trace)
338

    
339
    def process_mmap(self):
340
        self._memobj = bitwise.parse(self.mem_format, self._mmap)
341
    
342
    def _get_chan_enable(self, number):
343
        number = number - 1
344
        bytepos = number // 8
345
        bitpos = number  % 8
346
        chan_enable = self._memobj.chan_enable[bytepos]
347
        if chan_enable & ( 1 << bitpos ):
348
            return True
349
        else:
350
            return False
351

    
352
    def _set_chan_enable(self, number, enable):
353
        number = number - 1
354
        bytepos = number // 8
355
        bitpos = number  % 8
356
        chan_enable = self._memobj.chan_enable[bytepos]
357
        if enable:
358
            chan_enable = chan_enable | ( 1 << bitpos )  # enable
359
        else:
360
            chan_enable = chan_enable & ~ ( 1 << bitpos )  # disable            
361
        self._memobj.chan_enable[bytepos] = chan_enable
362
        
363
    def get_memory(self, number):
364
        mem = chirp_common.Memory()
365
        if isinstance(number, str):
366
            # special channel
367
            _mem = getattr(self._memobj, number)
368
            mem.number = - len(FT90_SPECIAL) + FT90_SPECIAL.index(number)
369
            mem.extd_number = number
370
            if re.match('^pms', mem.extd_number):
371
                # enable pms_XY channel flag
372
                _special_enables = self._memobj.special_enables
373
                mem.empty = not getattr(_special_enables, mem.extd_number + "_enable")
374
        else:
375
            # regular memory
376
            _mem = self._memobj.memory[number-1]
377
            mem.number = number
378
            mem.empty = not self._get_chan_enable(number)
379
        if mem.empty:
380
            return mem  # bail out, do not parse junk
381
        mem.freq = _mem.rxfreq * 10      
382
        mem.offset = _mem.txfreqoffset * 10
383
        if not _mem.tmode < len(FT90_TMODES):
384
            _mem.tmode = 0
385
        mem.tmode = FT90_TMODES[_mem.tmode]
386
        mem.rtone = FT90_TONES[_mem.tone]
387
        mem.dtcs = chirp_common.DTCS_CODES[_mem.dcstone]
388
        mem.mode = FT90_MODES[_mem.mode]
389
        mem.duplex = FT90_DUPLEX[_mem.shift]
390
        if mem.freq / 1000000 > 300:
391
            mem.power = FT90_POWER_LEVELS_UHF[_mem.power]
392
        else:
393
            mem.power = FT90_POWER_LEVELS_VHF[_mem.power]
394

    
395
        # radio has a known bug with 5khz step and squelch
396
        if _mem.step == 0 or _mem.step > len(FT90_STEPS)-1:
397
            _mem.step = 2
398
        mem.tuning_step = FT90_STEPS[_mem.step]
399
        mem.skip = _mem.skip and "S" or ""
400
        if not all(char in chirp_common.CHARSET_ASCII for char in str(_mem.name)):
401
            # dont display blank/junk name
402
            mem.name = ""
403
        else:
404
            mem.name = str(_mem.name)
405
        return mem
406

    
407
    def get_raw_memory(self, number):
408
        return repr(self._memobj.memory[number-1])
409
    
410
    def set_memory(self, mem):
411
        if mem.number < 0:      # special channels
412
            _mem = getattr(self._memobj, mem.extd_number)
413
            if re.match('^pms', mem.extd_number):
414
                # enable pms_XY channel flag
415
                _special_enables = self._memobj.special_enables
416
                setattr(_special_enables, mem.extd_number + "_enable", True)
417
        else:
418
            _mem = self._memobj.memory[mem.number - 1]
419
            self._set_chan_enable( mem.number, not mem.empty )
420
        _mem.skip = mem.skip == "S"
421
        # radio has a known bug with 5khz step and dead squelch
422
        if not mem.tuning_step or mem.tuning_step == FT90_STEPS[0]:
423
            _mem.step = 2
424
        else:
425
            _mem.step = FT90_STEPS.index(mem.tuning_step)
426
        _mem.rxfreq = mem.freq / 10
427
        # vfo will unlock if not in right band?
428
        if mem.freq > 300000000: 
429
            # uhf
430
            _mem.isUhf1 = 1
431
            _mem.isUhf2 = 1
432
            if mem.freq > 810000000:
433
                # uhf hiband
434
                _mem.isUhfHi = 1
435
            else:
436
                _mem.isUhfHi = 0
437
        else:
438
            # vhf
439
            _mem.isUhf1 = 0
440
            _mem.isUhf2 = 0
441
            _mem.isUhfHi = 0
442
        _mem.txfreqoffset = mem.offset / 10
443
        _mem.tone = FT90_TONES.index(mem.rtone)
444
        _mem.tmode = FT90_TMODES.index(mem.tmode)
445
        _mem.mode = FT90_MODES.index(mem.mode)
446
        _mem.shift = FT90_DUPLEX.index(mem.duplex)    
447
        _mem.dcstone = chirp_common.DTCS_CODES.index(mem.dtcs)
448
        _mem.step = FT90_STEPS.index(mem.tuning_step)
449
        _mem.shift = FT90_DUPLEX.index(mem.duplex)
450
        if mem.power:
451
            _mem.power = FT90_POWER_LEVELS_VHF.index(mem.power)
452
        else:
453
            _mem.power = 3  # default to low power
454
        if (len(mem.name) == 0):
455
            _mem.name = bytearray.fromhex("80ffffffffffff")
456
            _mem.showname = 0
457
        else:
458
            _mem.name = str(mem.name).ljust(7)
459
            _mem.showname = 1
460
            _mem.UseDefaultName = 0
461

    
462
    def _decode_cwid(self, cwidarr):
463
        cwid = ""
464
        if CHIRP_DEBUG:
465
            print "@ +_decode_cwid:"
466
        for byte in cwidarr.get_value():
467
            char = int(byte)
468
            if CHIRP_DEBUG:
469
                print char
470
            # bitwise wraps in quotes! get rid of those
471
            if char < len(FT90_CWID_CHARS):
472
                cwid += FT90_CWID_CHARS[char]
473
        return cwid
474

    
475
    def _encode_cwid(self, cwidarr):
476
        cwid = ""
477
        if CHIRP_DEBUG:
478
            print "@ _encode_cwid:"
479
        for char in cwidarr.get_value():
480
            cwid += chr(FT90_CWID_CHARS.index(char))
481
        if CHIRP_DEBUG:
482
            print cwid
483
        return cwid
484
    
485
    def _bbcd2dtmf(self, bcdarr, strlen = 16):
486
        # doing bbcd, but with support for ABCD*#
487
        if CHIRP_DEBUG:
488
            print bcdarr.get_value()
489
        string = ''.join("%02X" % b for b in bcdarr)
490
        if CHIRP_DEBUG:
491
            print "@_bbcd2dtmf, received: %s" % string
492
        string = string.replace('E','*').replace('F','#')
493
        if strlen <= 16:
494
            string = string[:strlen]
495
        return string
496
    
497
    def _dtmf2bbcd(self, dtmf):
498
        dtmfstr = dtmf.get_value()
499
        dtmfstr = dtmfstr.replace('*', 'E').replace('#', 'F')
500
        dtmfstr = str.ljust(dtmfstr.strip(), 16, "0" )
501
        bcdarr = list(bytearray.fromhex(dtmfstr))
502
        if CHIRP_DEBUG:
503
            print "@_dtmf2bbcd, sending: %s" % bcdarr
504
        return bcdarr
505
    
506
    def get_settings(self):
507
        _settings = self._memobj.settings
508
        basic = RadioSettingGroup("basic", "Basic")
509
        autodial = RadioSettingGroup("autodial", "AutoDial")
510
        keymaps = RadioSettingGroup("keymaps", "KeyMaps")
511
        top = RadioSettingGroup("top", "All Settings", basic, keymaps, autodial)
512
        
513
        rs = RadioSetting("beep", "Beep",
514
                          RadioSettingValueBoolean(_settings.beep))
515
        basic.append(rs)
516
        rs = RadioSetting("lock", "Lock",
517
                          RadioSettingValueBoolean(_settings.lock))
518
        basic.append(rs)
519
        rs = RadioSetting("ars", "Auto Repeater Shift",
520
                        RadioSettingValueBoolean(_settings.ars))
521
        basic.append(rs)
522
        rs = RadioSetting("txpwrsave", "TX Power Save",
523
                          RadioSettingValueBoolean(_settings.txpwrsave))
524
        basic.append(rs)
525
        rs = RadioSetting("txnarrow", "TX Narrow",
526
                          RadioSettingValueBoolean(_settings.txnarrow))
527
        basic.append(rs)
528
        options = ["Off", "S-3", "S-5", "S-Full"]
529
        rs = RadioSetting("rfsqlvl", "RF Squelch Level",
530
                          RadioSettingValueList(options,
531
                                        options[_settings.rfsqlvl]))
532
        basic.append(rs)
533
        options = ["Off", "Band A", "Band B", "Both"]
534
        rs = RadioSetting("pttlock", "PTT Lock",
535
                          RadioSettingValueList(options,
536
                                        options[_settings.pttlock]))
537
        basic.append(rs)
538
        
539
        rs = RadioSetting("cwid_en", "CWID Enable",
540
                          RadioSettingValueBoolean(_settings.cwid_en))
541
        basic.append(rs)
542
        
543
        cwid = RadioSettingValueString(0, 7, self._decode_cwid(_settings.cwid))
544
        cwid.set_charset(FT90_CWID_CHARS)
545
        rs = RadioSetting("cwid", "CWID", cwid)
546
        basic.append(rs)
547
        
548
        options = ["OFF"] + map(str, range(1, 12+1))
549
        rs = RadioSetting("apo", "APO time (hrs)",
550
                          RadioSettingValueList(options,
551
                                        options[_settings.apo]))
552
        basic.append(rs)
553
        
554
        options = ["Off"] + map(str, range(1, 60+1))
555
        rs = RadioSetting("tot", "Time Out Timer (mins)",
556
                        RadioSettingValueList(options,options[_settings.tot]))
557
        basic.append(rs)
558
        
559
        options = ["off", "Auto/TX", "Auto", "TX"]
560
        rs = RadioSetting("fancontrol", "Fan Control",
561
                        RadioSettingValueList(options,options[_settings.fancontrol]))
562
        basic.append(rs)
563
        
564
        keyopts = ["Scan Up", "Scan Down", "Repeater", "Reverse", "Tone Burst",
565
                        "Tx Power", "Home Ch", "VFO/MR", "Tone", "Priority"]
566
        rs = RadioSetting("key_lt", "Left Key",
567
                    RadioSettingValueList(keyopts,keyopts[_settings.key_lt]))
568
        keymaps.append(rs)
569
        rs = RadioSetting("key_rt", "Right Key",
570
                    RadioSettingValueList(keyopts,keyopts[_settings.key_rt]))
571
        keymaps.append(rs)
572
        rs = RadioSetting("key_p1", "P1 Key",
573
                    RadioSettingValueList(keyopts,keyopts[_settings.key_p1]))
574
        keymaps.append(rs)
575
        rs = RadioSetting("key_p2", "P2 Key",
576
                    RadioSettingValueList(keyopts,keyopts[_settings.key_p2]))
577
        keymaps.append(rs)
578
        rs = RadioSetting("key_acc", "ACC Key",
579
                    RadioSettingValueList(keyopts,keyopts[_settings.key_acc]))
580
        keymaps.append(rs)
581
        
582
        options = map(str, range(0,12+1))
583
        rs = RadioSetting("lcdcontrast", "LCD Contrast",
584
                        RadioSettingValueList(options,options[_settings.lcdcontrast]))
585
        basic.append(rs)
586
        
587
        options = ["off", "d4", "d3", "d2", "d1"]
588
        rs = RadioSetting("dimmer", "Dimmer",
589
                        RadioSettingValueList(options,options[_settings.dimmer]))
590
        basic.append(rs)
591
        
592
        options = ["TRX Normal", "RX Reverse", "TX Reverse", "TRX Reverse"]
593
        rs = RadioSetting("dcsmode", "DCS Mode",
594
                        RadioSettingValueList(options,options[_settings.dcsmode]))
595
        basic.append(rs)
596
        
597
        options = ["50 ms", "100 ms"]
598
        rs = RadioSetting("dtmfspeed", "DTMF Speed",
599
                        RadioSettingValueList(options,options[_settings.dtmfspeed]))
600
        autodial.append(rs)
601
        
602
        options = ["50 ms", "250 ms", "450 ms", "750 ms", "1 sec"]
603
        rs = RadioSetting("dtmftxdelay", "DTMF TX Delay",
604
                        RadioSettingValueList(options,options[_settings.dtmftxdelay]))
605
        autodial.append(rs)
606
        
607
        options = map(str,range(1,8+1))
608
        rs = RadioSetting("dtmf_active", "DTMF Active",
609
                RadioSettingValueList(options,options[_settings.dtmf_active]))
610
        autodial.append(rs)
611
        
612
        # setup 8 dtmf autodial entries
613
        for i in map(str, range(1,9)):
614
            objname = "dtmf" + i
615
            dtmfsetting = getattr(_settings, objname)
616
            dtmflen = getattr(_settings, objname + "_len")
617
            dtmfstr = self._bbcd2dtmf(dtmfsetting, dtmflen)
618
            dtmf = RadioSettingValueString(0, 16, dtmfstr)
619
            dtmf.set_charset(FT90_DTMF_CHARS + list(" "))
620
            rs = RadioSetting(objname, objname.upper(), dtmf)
621
            autodial.append(rs)
622
        
623
        return top
624
    
625
    def set_settings(self, uisettings):
626
        _settings = self._memobj.settings
627
        for element in uisettings:
628
            if not isinstance(element, RadioSetting):
629
                self.set_settings(element)
630
                continue
631
            if not element.changed():
632
                continue
633
            try:
634
                setting = element.get_name()
635
                oldval = getattr(_settings, setting)
636
                newval = element.value
637
                if setting == "cwid":
638
                    newval = self._encode_cwid(newval)
639
                if re.match('dtmf\d', setting):
640
                    # set dtmf length field and then get bcd dtmf
641
                    dtmfstrlen = len(str(newval).strip())
642
                    setattr(_settings, setting + "_len", dtmfstrlen)
643
                    newval = self._dtmf2bbcd(newval)
644
                if CHIRP_DEBUG:
645
                    print "Setting %s(%s) <= %s" % (setting,
646
                                    oldval, newval)
647
                setattr(_settings, setting, newval)
648
            except Exception, e:
649
                print element.get_name()
650
                raise
651

    
(13-13/15)