Project

General

Profile

Bug #1281 » uvb5.py

Jens Jensen, 12/03/2013 07:02 AM

 
1
# Copyright 2013 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 2 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
import struct
17
from chirp import chirp_common, directory, bitwise, memmap, errors, util
18
from chirp.settings import RadioSetting, RadioSettingGroup, \
19
                RadioSettingValueBoolean, RadioSettingValueList, \
20
                RadioSettingValueInteger, RadioSettingValueString, \
21
                RadioSettingValueFloat
22
from textwrap import dedent
23

    
24
mem_format = """
25
struct memory {
26
  lbcd freq[4];
27
  lbcd offset[4];
28
  u8 unknown1:2,
29
     txpol:1,
30
     rxpol:1,
31
     compander:1,
32
     unknown2:3;
33
  u8 rxtone;
34
  u8 txtone;
35
  u8 pttid:1,
36
     scanadd:1,
37
     isnarrow:1,
38
     bcl:1,
39
     highpower:1,
40
     revfreq:1,
41
     duplex:2;
42
  u8 step;
43
  u8 unknown[3];
44
};
45

    
46
#seekto 0x0000;
47
char ident[32];
48
u8 blank[16];
49
struct memory vfo1;
50
struct memory channels[99];
51
#seekto 0x0850;
52
struct memory vfo2;
53

    
54
#seekto 0x09D0;
55
u16 fm_presets[16];
56

    
57
#seekto 0x0A30;
58
struct {
59
  u8 name[5];
60
} names[99];
61

    
62
#seekto 0x0D30;
63
struct {
64
  u8 squelch;
65
  u8 freqmode_ab:1,
66
     save_funct:1,
67
     backlight:1,
68
     beep_tone:1,
69
     roger:1,
70
     tdr:1,
71
     scantype:2;
72
  u8 language:1,
73
     workmode_b:1,
74
     workmode_a:1,
75
     workmode_fm:1,
76
     voice_prompt:1,
77
     fm:1,
78
     pttid:2;
79
  u8 timeout;
80
  u8 mdf_b:2,
81
     mdf_a:2,
82
     unknown_1:2,
83
     txtdr:2;
84
  u8 sidetone;
85
  u8 vox;
86
} settings;
87

    
88
#seekto 0x0D50;
89
struct {
90
  u8 code[6];
91
} pttid;
92

    
93
#seekto 0x0F30;
94
struct {
95
  lbcd lower_vhf[2];
96
  lbcd upper_vhf[2];
97
  lbcd lower_uhf[2];
98
  lbcd upper_uhf[2];
99
} limits;
100

    
101
#seekto 0x0FF0;
102
struct {
103
  u8 vhfsquelch0;
104
  u8 vhfsquelch1;
105
  u8 vhfsquelch2;
106
  u8 vhfsquelch3;
107
  u8 vhfsquelch4;
108
  u8 vhfsquelch5;
109
  u8 vhfsquelch6;
110
  u8 vhfsquelch7;
111
  u8 vhfsquelch8;
112
  u8 vhfsquelch9;
113
  u8 unknown1[6];
114
  u8 uhfsquelch0;
115
  u8 uhfsquelch1;
116
  u8 uhfsquelch2;
117
  u8 uhfsquelch3;
118
  u8 uhfsquelch4;
119
  u8 uhfsquelch5;
120
  u8 uhfsquelch6;
121
  u8 uhfsquelch7;
122
  u8 uhfsquelch8;
123
  u8 uhfsquelch9;
124
  u8 unknown2[6];
125
  u8 vhfhipwr0;
126
  u8 vhfhipwr1;
127
  u8 vhfhipwr2;
128
  u8 vhfhipwr3;
129
  u8 vhfhipwr4;
130
  u8 vhfhipwr5;
131
  u8 vhfhipwr6;
132
  u8 vhfhipwr7;
133
  u8 vhflopwr0;
134
  u8 vhflopwr1;
135
  u8 vhflopwr2;
136
  u8 vhflopwr3;
137
  u8 vhflopwr4;
138
  u8 vhflopwr5;
139
  u8 vhflopwr6;
140
  u8 vhflopwr7;
141
  u8 uhfhipwr0;
142
  u8 uhfhipwr1;
143
  u8 uhfhipwr2;
144
  u8 uhfhipwr3;
145
  u8 uhfhipwr4;
146
  u8 uhfhipwr5;
147
  u8 uhfhipwr6;
148
  u8 uhfhipwr7;
149
  u8 uhflopwr0;
150
  u8 uhflopwr1;
151
  u8 uhflopwr2;
152
  u8 uhflopwr3;
153
  u8 uhflopwr4;
154
  u8 uhflopwr5;
155
  u8 uhflopwr6;
156
  u8 uhflopwr7;
157
} test;
158
"""
159

    
160
def do_ident(radio):
161
    radio.pipe.setTimeout(3)
162
    radio.pipe.write("PROGRAM")
163
    ack = radio.pipe.read(1)
164
    if ack != '\x06':
165
        raise errors.RadioError("Radio did not ack programming mode")
166
    radio.pipe.write("\x02")
167
    ident = radio.pipe.read(8)
168
    print util.hexprint(ident)
169
    if not ident.startswith('HKT511'):
170
        raise errors.RadioError("Unsupported model")
171
    radio.pipe.write("\x06")
172
    ack = radio.pipe.read(1)
173
    if ack != "\x06":
174
        raise errors.RadioError("Radio did not ack ident")
175

    
176
def do_status(radio, direction, addr):
177
    status = chirp_common.Status()
178
    status.msg = "Cloning %s radio" % direction
179
    status.cur = addr
180
    status.max = 0x1000
181
    radio.status_fn(status)
182

    
183
def do_download(radio):
184
    do_ident(radio)
185
    data = "KT511 Radio Program data v1.08\x00\x00"
186
    data += ("\x00" * 16)
187
    firstack = None
188
    for i in range(0, 0x1000, 16):
189
        frame = struct.pack(">cHB", "R", i, 16)
190
        radio.pipe.write(frame)
191
        result = radio.pipe.read(20)
192
        if frame[1:4] != result[1:4]:
193
            print util.hexprint(result)
194
            raise errors.RadioError("Invalid response for address 0x%04x" % i)
195
        radio.pipe.write("\x06")
196
        ack = radio.pipe.read(1)
197
        if not firstack:
198
            firstack = ack
199
        else:
200
            if not ack == firstack:
201
                print "first ack:", util.hexprint(firstack), \
202
                    "ack received:", util.hexprint(ack)
203
                raise errors.RadioError("Unexpected response")
204
        data += result[4:]
205
        do_status(radio, "from", i)
206

    
207
    return memmap.MemoryMap(data)
208

    
209
def do_upload(radio):
210
    do_ident(radio)
211
    data = radio._mmap[0x0030:]
212

    
213
    for i in range(0, 0x1000, 16):
214
        frame = struct.pack(">cHB", "W", i, 16)
215
        frame += data[i:i + 16]
216
        radio.pipe.write(frame)
217
        ack = radio.pipe.read(1)
218
        if ack != "\x06":
219
            raise errors.RadioError("Radio NAK'd block at address 0x%04x" % i)
220
        do_status(radio, "to", i)
221

    
222
DUPLEX = ["", "-", "+", 'off', "split"]
223
UVB5_STEPS = [5.00, 6.25, 10.0, 12.5, 20.0, 25.0]
224
CHARSET = "0123456789- ABCDEFGHIJKLMNOPQRSTUVWXYZ/_+*"
225
SPECIALS = {
226
    "VFO1": -2,
227
    "VFO2": -1,
228
    }
229
POWER_LEVELS = [chirp_common.PowerLevel("Low", watts=1),
230
                chirp_common.PowerLevel("High", watts=5)]
231

    
232
@directory.register
233
class BaofengUVB5(chirp_common.CloneModeRadio,
234
        chirp_common.ExperimentalRadio):
235
    """Baofeng UV-B5"""
236
    VENDOR = "Baofeng"
237
    MODEL = "UV-B5"
238
    BAUD_RATE = 9600
239

    
240
    _memsize = 0x1000
241

    
242
    @classmethod
243
    def get_prompts(cls):
244
        rp = chirp_common.RadioPrompts()
245
        rp.experimental = ('This version of the UV-B5 driver allows you to '
246
                'modify the Test Mode settings of your radio. This has been '
247
                'tested and reports from other users indicate that it is a '
248
                'safe thing to do. However, modifications to these values may '
249
                'have unintended consequences, including damage to your '
250
                'device. You have been warned. Proceed at your own risk!')
251
        rp.pre_download = _(dedent("""\
252
            1. Turn radio off.
253
            2. Connect cable to mic/spkr connector.
254
            3. Make sure connector is firmly connected.
255
            4. Turn radio on.
256
            5. Ensure that the radio is tuned to channel with no activity.
257
            6. Click OK to download image from device."""))
258
        rp.pre_upload = _(dedent("""\
259
            1. Turn radio off.
260
            2. Connect cable to mic/spkr connector.
261
            3. Make sure connector is firmly connected.
262
            4. Turn radio on.
263
            5. Ensure that the radio is tuned to channel with no activity.
264
            6. Click OK to upload image to device."""))
265
        return rp
266
        
267
    def get_features(self):
268
        rf = chirp_common.RadioFeatures()
269
        rf.has_settings = True
270
        rf.has_cross = True
271
        rf.has_rx_dtcs = True
272
        rf.valid_tmodes = ["", "Tone", "TSQL", "DTCS", "Cross"]
273
        rf.valid_cross_modes = ["Tone->Tone", "Tone->DTCS", "DTCS->Tone",
274
                                "->Tone", "->DTCS", "DTCS->", "DTCS->DTCS"]
275
        rf.valid_duplexes = DUPLEX
276
        rf.can_odd_split = True
277
        rf.valid_skips = ["", "S"]
278
        rf.valid_characters = CHARSET
279
        rf.valid_name_length = 5
280
        rf.valid_bands = [(136000000, 174000000),
281
                          (400000000, 520000000)]
282
        rf.valid_modes = ["FM", "NFM"]
283
        rf.valid_special_chans = SPECIALS.keys()
284
        rf.valid_power_levels = POWER_LEVELS
285
        rf.has_ctone = True
286
        rf.has_bank = False
287
        rf.has_tuning_step = True
288
        rf.valid_tuning_steps = UVB5_STEPS
289
        rf.memory_bounds = (1, 99)
290
        return rf
291

    
292
    def sync_in(self):
293
        try:
294
            self._mmap = do_download(self)
295
        except errors.RadioError:
296
            raise
297
        except Exception, e:
298
            raise errors.RadioError("Failed to communicate with radio: %s" % e)
299
        self.process_mmap()
300

    
301
    def sync_out(self):
302
        try:
303
            do_upload(self)
304
        except errors.RadioError:
305
            raise
306
        except Exception, e:
307
            raise errors.RadioError("Failed to communicate with radio: %s" % e)
308

    
309
    def process_mmap(self):
310
        self._memobj = bitwise.parse(mem_format, self._mmap)
311

    
312
    def get_raw_memory(self, number):
313
        return repr(self._memobj.channels[number - 1])
314

    
315
    def _decode_tone(self, value, flag):
316
        if value > 50:
317
            mode = 'DTCS'
318
            val = chirp_common.DTCS_CODES[value - 51]
319
            pol = flag and 'R' or 'N'
320
        elif value:
321
            mode = 'Tone'
322
            val = chirp_common.TONES[value - 1]
323
            pol = None
324
        else:
325
            mode = val = pol = None
326

    
327
        return mode, val, pol
328

    
329
    def _encode_tone(self, _mem, which, mode, val, pol):
330
        def _set(field, value):
331
            setattr(_mem, "%s%s" % (which, field), value)
332

    
333
        _set("pol", 0)
334
        if mode == "Tone":
335
            _set("tone", chirp_common.TONES.index(val) + 1)
336
        elif mode == "DTCS":
337
            _set("tone", chirp_common.DTCS_CODES.index(val) + 51)
338
            _set("pol", pol == "R")
339
        else:
340
            _set("tone", 0)
341

    
342
    def _get_memobjs(self, number):
343
        if isinstance(number, str):
344
            return (getattr(self._memobj, number.lower()), None)
345
        elif number < 0:
346
            for k, v in SPECIALS.items():
347
                if number == v:
348
                    return (getattr(self._memobj, k.lower()), None)
349
        else:
350
            return (self._memobj.channels[number - 1],
351
                    self._memobj.names[number - 1].name)
352

    
353
    def get_memory(self, number):
354
        _mem, _nam = self._get_memobjs(number)
355
        mem = chirp_common.Memory()
356
        if isinstance(number, str):
357
            mem.number = SPECIALS[number]
358
            mem.extd_number = number
359
        else:
360
            mem.number = number
361

    
362
        if _mem.freq.get_raw()[0] == "\xFF":
363
            mem.empty = True
364
            return mem
365

    
366
        mem.freq = int(_mem.freq) * 10
367
        mem.offset = int(_mem.offset) * 10
368

    
369
        chirp_common.split_tone_decode(
370
            mem,
371
            self._decode_tone(_mem.txtone, _mem.txpol),
372
            self._decode_tone(_mem.rxtone, _mem.rxpol))
373

    
374
        if _mem.step < 0x06:
375
            mem.tuning_step = UVB5_STEPS[_mem.step]
376
        else:
377
            _mem.step = 0x00
378
            mem.tuning_step = UVB5_STEPS[0]
379
        mem.duplex = DUPLEX[_mem.duplex]
380
        mem.mode = _mem.isnarrow and "NFM" or "FM"
381
        mem.skip = "" if _mem.scanadd else "S"
382
        mem.power = POWER_LEVELS[_mem.highpower]
383

    
384
        if mem.freq == mem.offset and mem.duplex == "-":
385
           mem.duplex = "off"
386
           mem.offset = 0
387

    
388
        if _nam:
389
            for char in _nam:
390
                try:
391
                    mem.name += CHARSET[char]
392
                except IndexError:
393
                    break
394
            mem.name = mem.name.rstrip()
395

    
396
        mem.extra = RadioSettingGroup("Extra", "extra")
397

    
398
        rs = RadioSetting("bcl", "BCL",
399
                          RadioSettingValueBoolean(_mem.bcl))
400
        mem.extra.append(rs)
401

    
402
        rs = RadioSetting("revfreq", "Reverse Duplex",
403
                          RadioSettingValueBoolean(_mem.revfreq))
404
        mem.extra.append(rs)
405

    
406
        rs = RadioSetting("pttid", "PTT ID",
407
                          RadioSettingValueBoolean(_mem.pttid))
408
        mem.extra.append(rs)
409

    
410
        rs = RadioSetting("compander", "Compander",
411
                          RadioSettingValueBoolean(_mem.compander))
412
        mem.extra.append(rs)
413

    
414
        return mem
415

    
416
    def set_memory(self, mem):
417
        _mem, _nam = self._get_memobjs(mem.number)
418

    
419
        if mem.empty:
420
            if _nam is None:
421
                raise errors.InvalidValueError("VFO channels can not be empty")
422
            _mem.set_raw("\xFF" * 16)
423
            return
424

    
425
        if _mem.get_raw() == ("\xFF" * 16):
426
            _mem.set_raw("\x00" * 13 + "\xFF" * 3)
427

    
428
        _mem.freq = mem.freq / 10
429

    
430
        if mem.duplex == "off":
431
            _mem.duplex = DUPLEX.index("-")
432
            _mem.offset = _mem.freq
433
        elif mem.duplex == "split":
434
            diff = mem.offset - mem.freq 
435
            _mem.duplex = DUPLEX.index("-") if diff < 0 else DUPLEX.index("+")
436
            _mem.offset = abs(diff) / 10
437
        else:
438
            _mem.offset = mem.offset / 10
439
            _mem.duplex = DUPLEX.index(mem.duplex)
440

    
441
        tx, rx = chirp_common.split_tone_encode(mem)
442
        self._encode_tone(_mem, 'tx', *tx)
443
        self._encode_tone(_mem, 'rx', *rx)
444

    
445
        _mem.step = UVB5_STEPS.index(mem.tuning_step)
446
        _mem.isnarrow = mem.mode == "NFM"
447
        _mem.scanadd = mem.skip == ""
448
        _mem.highpower = mem.power == POWER_LEVELS[1]
449

    
450
        if _nam:
451
            for i in range(0, 5):
452
                try:
453
                    _nam[i] = CHARSET.index(mem.name[i])
454
                except IndexError:
455
                    _nam[i] = 0xFF
456

    
457
        for setting in mem.extra:
458
            setattr(_mem, setting.get_name(), setting.value)
459

    
460
    def validate_memory(self, mem):
461
        msgs = chirp_common.CloneModeRadio.validate_memory(self, mem)
462

    
463
        if (mem.duplex == "split" and abs(mem.freq - mem.offset) > 69995000) or \
464
                (mem.duplex in ["+", "-"] and mem.offset > 69995000) :
465
            msgs.append(chirp_common.ValidationError(
466
                    "Max split is 69.995MHz"))
467
        return msgs
468

    
469

    
470
    def get_settings(self):
471
        basic = RadioSettingGroup("basic", "Basic Settings")
472
        group = RadioSettingGroup("top", "All Settings", basic)
473

    
474
        options = ["Time", "Carrier", "Search"]
475
        rs = RadioSetting("scantype", "Scan Type",
476
                          RadioSettingValueList(options,
477
                                        options[self._memobj.settings.scantype]))
478
        basic.append(rs)
479

    
480
        options = ["%s min" % x for x in range(1, 8)]
481
        options.insert(0, "Off")
482
        rs = RadioSetting("timeout", "Time Out Timer",
483
                          RadioSettingValueList(options,
484
                                        options[self._memobj.settings.timeout]))
485
        basic.append(rs)
486

    
487
        options = ["A", "B"]
488
        rs = RadioSetting("freqmode_ab", "Frequency Mode",
489
                          RadioSettingValueList(options,
490
                                        options[self._memobj.settings.freqmode_ab]))
491
        basic.append(rs)
492

    
493
        options = ["Frequency Mode", "Channel Mode"]
494
        rs = RadioSetting("workmode_a", "Radio Work Mode(A)",
495
                          RadioSettingValueList(options,
496
                                        options[self._memobj.settings.workmode_a]))
497
        basic.append(rs)
498

    
499
        rs = RadioSetting("workmode_b", "Radio Work Mode(B)",
500
                          RadioSettingValueList(options,
501
                                        options[self._memobj.settings.workmode_b]))
502
        basic.append(rs)
503

    
504
        options = ["Frequency", "Name", "Channel"]
505
        rs = RadioSetting("mdf_a", "Display Format(F1)",
506
                          RadioSettingValueList(options,
507
                                        options[self._memobj.settings.mdf_a]))
508
        basic.append(rs)
509

    
510
        rs = RadioSetting("mdf_b", "Display Format(F2)",
511
                          RadioSettingValueList(options,
512
                                        options[self._memobj.settings.mdf_b]))
513
        basic.append(rs)
514

    
515
        options = ["Off", "BOT", "EOT", "Both"]
516
        rs = RadioSetting("pttid", "PTT-ID",
517
                          RadioSettingValueList(options,
518
                                        options[self._memobj.settings.pttid]))
519
        basic.append(rs)
520

    
521
        dtmfchars = "0123456789ABCD"
522
        _codeobj = self._memobj.pttid.code
523
        _code = "".join([dtmfchars[x] for x in _codeobj if int(x) < 0x1F])
524
        val = RadioSettingValueString(0, 6, _code, False)
525
        val.set_charset(dtmfchars)
526
        rs = RadioSetting("pttid.code", "PTT-ID Code", val)
527
        def apply_code(setting, obj):
528
            code = []
529
            for j in range(0, 6):
530
                try:
531
                    code.append(dtmfchars.index(str(setting.value)[j]))
532
                except IndexError:
533
                    code.append(0xFF)
534
            obj.code = code
535
        rs.set_apply_callback(apply_code, self._memobj.pttid)
536
        basic.append(rs)
537

    
538
        rs = RadioSetting("squelch", "Squelch Level",
539
                          RadioSettingValueInteger(0, 9, self._memobj.settings.squelch))
540
        basic.append(rs)
541

    
542
        rs = RadioSetting("vox", "VOX Level",
543
                          RadioSettingValueInteger(0, 9, self._memobj.settings.vox))
544
        basic.append(rs)
545

    
546
        options = ["Frequency Mode", "Channel Mode"]
547
        rs = RadioSetting("workmode_fm", "FM Work Mode",
548
                          RadioSettingValueList(options,
549
                                        options[self._memobj.settings.workmode_fm]))
550
        basic.append(rs)
551

    
552
        options = ["Current Frequency", "F1 Frequency", "F2 Frequency"]
553
        rs = RadioSetting("txtdr", "Dual Standby TX Priority",
554
                          RadioSettingValueList(options,
555
                                        options[self._memobj.settings.txtdr]))
556
        basic.append(rs)
557

    
558
        options = ["English", "Chinese"]
559
        rs = RadioSetting("language", "Language",
560
                          RadioSettingValueList(options,
561
                                        options[self._memobj.settings.language]))
562
        basic.append(rs)
563

    
564
        rs = RadioSetting("tdr", "Dual Standby",
565
                          RadioSettingValueBoolean(self._memobj.settings.tdr))
566
        basic.append(rs)
567

    
568
        rs = RadioSetting("roger", "Roger Beep",
569
                          RadioSettingValueBoolean(self._memobj.settings.roger))
570
        basic.append(rs)
571

    
572
        rs = RadioSetting("backlight", "Backlight",
573
                          RadioSettingValueBoolean(self._memobj.settings.backlight))
574
        basic.append(rs)
575

    
576
        rs = RadioSetting("save_funct", "Save Mode",
577
                          RadioSettingValueBoolean(self._memobj.settings.save_funct))
578
        basic.append(rs)
579

    
580
        rs = RadioSetting("fm", "FM Function",
581
                          RadioSettingValueBoolean(self._memobj.settings.fm))
582
        basic.append(rs)
583

    
584
        options = ["Enabled", "Disabled"]
585
        rs = RadioSetting("beep_tone", "Beep Prompt",
586
                          RadioSettingValueList(options,
587
                                        options[self._memobj.settings.beep_tone]))
588
        basic.append(rs)
589

    
590
        rs = RadioSetting("voice_prompt", "Voice Prompt",
591
                          RadioSettingValueBoolean(self._memobj.settings.voice_prompt))
592
        basic.append(rs)
593

    
594
        rs = RadioSetting("sidetone", "DTMF Side Tone",
595
                          RadioSettingValueBoolean(self._memobj.settings.sidetone))
596
        basic.append(rs)
597

    
598
        _limit = int(self._memobj.limits.lower_vhf) / 10
599
        rs = RadioSetting("limits.lower_vhf", "VHF Lower Limit (MHz)",
600
                          RadioSettingValueInteger(136, 174, _limit))
601
        def apply_limit(setting, obj):
602
            value = int(setting.value) * 10
603
            obj.lower_vhf = value
604
        rs.set_apply_callback(apply_limit, self._memobj.limits)
605
        basic.append(rs)
606

    
607
        _limit = int(self._memobj.limits.upper_vhf) / 10
608
        rs = RadioSetting("limits.upper_vhf", "VHF Upper Limit (MHz)",
609
                          RadioSettingValueInteger(136, 174, _limit))
610
        def apply_limit(setting, obj):
611
            value = int(setting.value) * 10
612
            obj.upper_vhf = value
613
        rs.set_apply_callback(apply_limit, self._memobj.limits)
614
        basic.append(rs)
615

    
616
        _limit = int(self._memobj.limits.lower_uhf) / 10
617
        rs = RadioSetting("limits.lower_uhf", "UHF Lower Limit (MHz)",
618
                          RadioSettingValueInteger(400, 520, _limit))
619
        def apply_limit(setting, obj):
620
            value = int(setting.value) * 10
621
            obj.lower_uhf = value
622
        rs.set_apply_callback(apply_limit, self._memobj.limits)
623
        basic.append(rs)
624

    
625
        _limit = int(self._memobj.limits.upper_uhf) / 10
626
        rs = RadioSetting("limits.upper_uhf", "UHF Upper Limit (MHz)",
627
                          RadioSettingValueInteger(400, 520, _limit))
628
        def apply_limit(setting, obj):
629
            value = int(setting.value) * 10
630
            obj.upper_uhf = value
631
        rs.set_apply_callback(apply_limit, self._memobj.limits)
632
        basic.append(rs)
633

    
634
        fm_preset = RadioSettingGroup("fm_preset", "FM Radio Presets")
635
        group.append(fm_preset)
636

    
637
        for i in range(0, 16):
638
            if self._memobj.fm_presets[i] < 0x01AF:
639
                used = True
640
                preset = self._memobj.fm_presets[i] / 10.0 + 65
641
            else:
642
                used = False
643
                preset = 65
644
            rs = RadioSetting("fm_presets_%1i" % i, "FM Preset %i" % (i + 1),
645
                          RadioSettingValueBoolean(used),
646
                          RadioSettingValueFloat(65, 108, preset, 0.1, 1))
647
            fm_preset.append(rs)
648

    
649
        testmode = RadioSettingGroup("testmode", "Test Mode Settings")
650
        group.append(testmode)
651

    
652
        vhfdata = ["136-139", "140-144", "145-149", "150-154",
653
                   "155-159", "160-164", "165-169", "170-174"]
654
        uhfdata = ["400-409", "410-419", "420-429", "430-439",
655
                   "440-449", "450-459", "460-469", "470-479"]
656
        powernamedata = ["Hi", "Lo"]
657
        powerkeydata = ["hipwr", "lopwr"]
658

    
659
        for power in range (0, 2):
660
            for index in range(0, 8):
661
                key = "test.vhf%s%i" % (powerkeydata[power], index)
662
                name = "%s Mhz %s Power" % (vhfdata[index],
663
                                            powernamedata[power])
664
                rs = RadioSetting(key, name, RadioSettingValueInteger(0, 255,
665
                        getattr(self._memobj.test, "vhf%s%i"
666
                                % (powerkeydata[power], index))))
667
                testmode.append(rs)
668

    
669
        for power in range (0, 2):
670
            for index in range(0, 8):
671
                key = "test.uhf%s%i" % (powerkeydata[power], index)
672
                name = "%s Mhz %s Power" % (uhfdata[index],
673
                                            powernamedata[power])
674
                rs = RadioSetting(key, name, RadioSettingValueInteger(0, 255,
675
                        getattr(self._memobj.test, "uhf%s%i"
676
                                % (powerkeydata[power], index))))
677
                testmode.append(rs)
678

    
679
        for band in ["vhf", "uhf"]:
680
            for index in range(0, 10):
681
                key = "test.%ssquelch%i" % (band, index)
682
                name = "%s Squelch %i" % (band.upper(), index)
683
                rs = RadioSetting(key, name, RadioSettingValueInteger(0, 255,
684
                        getattr(self._memobj.test, "%ssquelch%i" 
685
                                % (band, index))))
686
                testmode.append(rs)
687

    
688
        return group
689

    
690
    def set_settings(self, settings):
691
        _settings = self._memobj.settings
692
        for element in settings:
693
            if not isinstance(element, RadioSetting):
694
                if element.get_name() == "fm_preset" :
695
                    self._set_fm_preset(element)
696
                else:
697
                    self.set_settings(element)
698
                    continue
699
            else:
700
                try:
701
                    name = element.get_name()
702
                    if "." in name:
703
                        bits = name.split(".")
704
                        obj = self._memobj
705
                        for bit in bits[:-1]:
706
                            if "/" in bit:
707
                                bit, index = bit.split("/", 1)
708
                                index = int(index)
709
                                obj = getattr(obj, bit)[index]
710
                            else:
711
                                obj = getattr(obj, bit)
712
                        setting = bits[-1]
713
                    else:
714
                        obj = _settings
715
                        setting = element.get_name()
716

    
717
                    if element.has_apply_callback():
718
                        print "Using apply callback"
719
                        element.run_apply_callback()
720
                    else:
721
                        print "Setting %s = %s" % (setting, element.value)
722
                        setattr(obj, setting, element.value)
723
                except Exception, e:
724
                    print element.get_name()
725
                    raise
726

    
727
    def _set_fm_preset(self, settings):
728
        for element in settings:
729
            try:
730
                index = (int(element.get_name().split("_")[-1]))
731
                val = element.value
732
                if val[0].get_value():
733
                    value = int(val[1].get_value() * 10 - 650)
734
                else:
735
                    value = 0x01AF
736
                print "Setting fm_presets[%1i] = %s" % (index, value)
737
                setting = self._memobj.fm_presets
738
                setting[index] = value
739
            except Exception, e:
740
                print element.get_name()
741
                raise
742

    
743

    
744
    @classmethod
745
    def match_model(cls, filedata, filename):
746
        return (filedata.startswith("KT511 Radio Program data") and
747
                len(filedata) == (cls._memsize + 0x30))
(7-7/7)