Project

General

Profile

Bug #2751 ยป uvb5.py

UV-B5 bug fix - Edward Lisle, 10/12/2015 01:42 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
import logging
18
from chirp import chirp_common, directory, bitwise, memmap, errors, util
19
from chirp.settings import RadioSetting, RadioSettingGroup, \
20
                RadioSettingValueBoolean, RadioSettingValueList, \
21
                RadioSettingValueInteger, RadioSettingValueString, \
22
                RadioSettingValueFloat, RadioSettings
23
from textwrap import dedent
24

    
25
LOG = logging.getLogger(__name__)
26

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

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

    
57
#seekto 0x09D0;
58
u16 fm_presets[16];
59

    
60
#seekto 0x0A30;
61
struct {
62
  u8 name[5];
63
} names[99];
64

    
65
#seekto 0x0D30;
66
struct {
67
  u8 squelch;
68
  u8 freqmode_ab:1,
69
     save_funct:1,
70
     backlight:1,
71
     beep_tone_disabled:1,
72
     roger:1,
73
     tdr:1,
74
     scantype:2;
75
  u8 language:1,
76
     workmode_b:1,
77
     workmode_a:1,
78
     workmode_fm:1,
79
     voice_prompt:1,
80
     fm:1,
81
     pttid:2;
82
  u8 unknown_0:5,
83
     timeout:3;
84
  u8 mdf_b:2,
85
     mdf_a:2,
86
     unknown_1:2,
87
     txtdr:2;
88
  u8 unknown_2:4,
89
     ste_disabled:1,
90
     unknown_3:2,
91
     sidetone:1;
92
  u8 vox;
93
  u8 unk1;
94
  u8 mem_chan_a;
95
  u16 fm_vfo;
96
  u8 unk4;
97
  u8 unk5;
98
  u8 mem_chan_b;
99
  u8 unk6;
100
  u8 last_menu; // number of last menu item accessed
101
} settings;
102

    
103
#seekto 0x0D50;
104
struct {
105
  u8 code[6];
106
} pttid;
107

    
108
#seekto 0x0F30;
109
struct {
110
  lbcd lower_vhf[2];
111
  lbcd upper_vhf[2];
112
  lbcd lower_uhf[2];
113
  lbcd upper_uhf[2];
114
} limits;
115

    
116
#seekto 0x0FF0;
117
struct {
118
  u8 vhfsquelch0;
119
  u8 vhfsquelch1;
120
  u8 vhfsquelch2;
121
  u8 vhfsquelch3;
122
  u8 vhfsquelch4;
123
  u8 vhfsquelch5;
124
  u8 vhfsquelch6;
125
  u8 vhfsquelch7;
126
  u8 vhfsquelch8;
127
  u8 vhfsquelch9;
128
  u8 unknown1[6];
129
  u8 uhfsquelch0;
130
  u8 uhfsquelch1;
131
  u8 uhfsquelch2;
132
  u8 uhfsquelch3;
133
  u8 uhfsquelch4;
134
  u8 uhfsquelch5;
135
  u8 uhfsquelch6;
136
  u8 uhfsquelch7;
137
  u8 uhfsquelch8;
138
  u8 uhfsquelch9;
139
  u8 unknown2[6];
140
  u8 vhfhipwr0;
141
  u8 vhfhipwr1;
142
  u8 vhfhipwr2;
143
  u8 vhfhipwr3;
144
  u8 vhfhipwr4;
145
  u8 vhfhipwr5;
146
  u8 vhfhipwr6;
147
  u8 vhfhipwr7;
148
  u8 vhflopwr0;
149
  u8 vhflopwr1;
150
  u8 vhflopwr2;
151
  u8 vhflopwr3;
152
  u8 vhflopwr4;
153
  u8 vhflopwr5;
154
  u8 vhflopwr6;
155
  u8 vhflopwr7;
156
  u8 uhfhipwr0;
157
  u8 uhfhipwr1;
158
  u8 uhfhipwr2;
159
  u8 uhfhipwr3;
160
  u8 uhfhipwr4;
161
  u8 uhfhipwr5;
162
  u8 uhfhipwr6;
163
  u8 uhfhipwr7;
164
  u8 uhflopwr0;
165
  u8 uhflopwr1;
166
  u8 uhflopwr2;
167
  u8 uhflopwr3;
168
  u8 uhflopwr4;
169
  u8 uhflopwr5;
170
  u8 uhflopwr6;
171
  u8 uhflopwr7;
172
} test;
173
"""
174

    
175

    
176
def do_ident(radio):
177
    radio.pipe.setTimeout(3)
178
    ack = '\x00'
179
    radio.pipe.write("\x05PROGRAM")
180
    while ack != '\x06':
181
        ack = radio.pipe.read(1)
182
        if len(ack) == 0:
183
        # We timed out without seeing the expected byte
184
        	raise errors.RadioError("Radio did not ack programming mode")
185
        	return
186
    radio.pipe.write("\x02")
187
    ident = radio.pipe.read(8)
188
    LOG.debug(util.hexprint(ident))
189
    if not ident.startswith('HKT511'):
190
        raise errors.RadioError("Unsupported model")
191
    radio.pipe.write("\x06")
192
    ack = radio.pipe.read(1)
193
    if ack != "\x06":
194
        raise errors.RadioError("Radio did not ack ident")
195

    
196

    
197
def do_status(radio, direction, addr):
198
    status = chirp_common.Status()
199
    status.msg = "Cloning %s radio" % direction
200
    status.cur = addr
201
    status.max = 0x1000
202
    radio.status_fn(status)
203

    
204

    
205
def do_download(radio):
206
    do_ident(radio)
207
    data = "KT511 Radio Program data v1.08\x00\x00"
208
    data += ("\x00" * 16)
209
    firstack = None
210
    for i in range(0, 0x1000, 16):
211
        frame = struct.pack(">cHB", "R", i, 16)
212
        radio.pipe.write(frame)
213
        result = radio.pipe.read(20)
214
        if frame[1:4] != result[1:4]:
215
            LOG.debug(util.hexprint(result))
216
            raise errors.RadioError("Invalid response for address 0x%04x" % i)
217
        radio.pipe.write("\x06")
218
        ack = radio.pipe.read(1)
219
        if not firstack:
220
            firstack = ack
221
        else:
222
            if not ack == firstack:
223
                LOG.debug("first ack: %s ack received: %s",
224
                          util.hexprint(firstack), util.hexprint(ack))
225
                raise errors.RadioError("Unexpected response")
226
        data += result[4:]
227
        do_status(radio, "from", i)
228

    
229
    return memmap.MemoryMap(data)
230

    
231

    
232
def do_upload(radio):
233
    do_ident(radio)
234
    data = radio._mmap[0x0030:]
235

    
236
    for i in range(0, 0x1000, 16):
237
        frame = struct.pack(">cHB", "W", i, 16)
238
        frame += data[i:i + 16]
239
        radio.pipe.write(frame)
240
        ack = radio.pipe.read(1)
241
        if ack != "\x06":
242
            raise errors.RadioError("Radio NAK'd block at address 0x%04x" % i)
243
        do_status(radio, "to", i)
244

    
245
DUPLEX = ["", "-", "+", 'off', "split"]
246
UVB5_STEPS = [5.00, 6.25, 10.0, 12.5, 20.0, 25.0]
247
CHARSET = "0123456789- ABCDEFGHIJKLMNOPQRSTUVWXYZ/_+*"
248
SPECIALS = {
249
    "VFO1": -2,
250
    "VFO2": -1,
251
    }
252
POWER_LEVELS = [chirp_common.PowerLevel("Low", watts=1),
253
                chirp_common.PowerLevel("High", watts=5)]
254

    
255

    
256
@directory.register
257
class BaofengUVB5(chirp_common.CloneModeRadio,
258
                  chirp_common.ExperimentalRadio):
259
    """Baofeng UV-B5"""
260
    VENDOR = "Baofeng"
261
    MODEL = "UV-B5"
262
    BAUD_RATE = 9600
263

    
264
    _memsize = 0x1000
265

    
266
    @classmethod
267
    def get_prompts(cls):
268
        rp = chirp_common.RadioPrompts()
269
        rp.experimental = \
270
            ('This version of the UV-B5 driver allows you to '
271
             'modify the Test Mode settings of your radio. This has been '
272
             'tested and reports from other users indicate that it is a '
273
             'safe thing to do. However, modifications to these values may '
274
             'have unintended consequences, including damage to your '
275
             'device. You have been warned. Proceed at your own risk!')
276
        rp.pre_download = _(dedent("""\
277
            1. Turn radio off.
278
            2. Connect cable to mic/spkr connector.
279
            3. Make sure connector is firmly connected.
280
            4. Turn radio on.
281
            5. Ensure that the radio is tuned to channel with no activity.
282
            6. Click OK to download image from device."""))
283
        rp.pre_upload = _(dedent("""\
284
            1. Turn radio off.
285
            2. Connect cable to mic/spkr connector.
286
            3. Make sure connector is firmly connected.
287
            4. Turn radio on.
288
            5. Ensure that the radio is tuned to channel with no activity.
289
            6. Click OK to upload image to device."""))
290
        return rp
291

    
292
    def get_features(self):
293
        rf = chirp_common.RadioFeatures()
294
        rf.has_settings = True
295
        rf.has_cross = True
296
        rf.has_rx_dtcs = True
297
        rf.valid_tmodes = ["", "Tone", "TSQL", "DTCS", "Cross"]
298
        rf.valid_cross_modes = ["Tone->Tone", "Tone->DTCS", "DTCS->Tone",
299
                                "->Tone", "->DTCS", "DTCS->", "DTCS->DTCS"]
300
        rf.valid_duplexes = DUPLEX
301
        rf.can_odd_split = True
302
        rf.valid_skips = ["", "S"]
303
        rf.valid_characters = CHARSET
304
        rf.valid_name_length = 5
305
        rf.valid_bands = [(130000000, 175000000),
306
                          (220000000, 269000000),
307
                          (400000000, 520000000)]
308
        rf.valid_modes = ["FM", "NFM"]
309
        rf.valid_special_chans = SPECIALS.keys()
310
        rf.valid_power_levels = POWER_LEVELS
311
        rf.has_ctone = True
312
        rf.has_bank = False
313
        rf.has_tuning_step = False
314
        rf.memory_bounds = (1, 99)
315
        return rf
316

    
317
    def sync_in(self):
318
        try:
319
            self._mmap = do_download(self)
320
        except errors.RadioError:
321
            raise
322
        except Exception, e:
323
            raise errors.RadioError("Failed to communicate with radio: %s" % e)
324
        self.process_mmap()
325

    
326
    def sync_out(self):
327
        try:
328
            do_upload(self)
329
        except errors.RadioError:
330
            raise
331
        except Exception, e:
332
            raise errors.RadioError("Failed to communicate with radio: %s" % e)
333

    
334
    def process_mmap(self):
335
        self._memobj = bitwise.parse(mem_format, self._mmap)
336

    
337
    def get_raw_memory(self, number):
338
        return repr(self._memobj.channels[number - 1])
339

    
340
    def _decode_tone(self, value, flag):
341
        if value > 155:
342
            mode = val = pol = None
343
        elif value > 50:
344
            mode = 'DTCS'
345
            val = chirp_common.DTCS_CODES[value - 51]
346
            pol = flag and 'R' or 'N'
347
        elif value:
348
            mode = 'Tone'
349
            val = chirp_common.TONES[value - 1]
350
            pol = None
351
        else:
352
            mode = val = pol = None
353

    
354
        return mode, val, pol
355

    
356
    def _encode_tone(self, _mem, which, mode, val, pol):
357
        def _set(field, value):
358
            setattr(_mem, "%s%s" % (which, field), value)
359

    
360
        _set("pol", 0)
361
        if mode == "Tone":
362
            _set("tone", chirp_common.TONES.index(val) + 1)
363
        elif mode == "DTCS":
364
            _set("tone", chirp_common.DTCS_CODES.index(val) + 51)
365
            _set("pol", pol == "R")
366
        else:
367
            _set("tone", 0)
368

    
369
    def _get_memobjs(self, number):
370
        if isinstance(number, str):
371
            return (getattr(self._memobj, number.lower()), None)
372
        elif number < 0:
373
            for k, v in SPECIALS.items():
374
                if number == v:
375
                    return (getattr(self._memobj, k.lower()), None)
376
        else:
377
            return (self._memobj.channels[number - 1],
378
                    self._memobj.names[number - 1].name)
379

    
380
    def get_memory(self, number):
381
        _mem, _nam = self._get_memobjs(number)
382
        mem = chirp_common.Memory()
383
        if isinstance(number, str):
384
            mem.number = SPECIALS[number]
385
            mem.extd_number = number
386
        else:
387
            mem.number = number
388

    
389
        if _mem.freq.get_raw()[0] == "\xFF":
390
            mem.empty = True
391
            return mem
392

    
393
        mem.freq = int(_mem.freq) * 10
394
        mem.offset = int(_mem.offset) * 10
395

    
396
        chirp_common.split_tone_decode(
397
            mem,
398
            self._decode_tone(_mem.txtone, _mem.txpol),
399
            self._decode_tone(_mem.rxtone, _mem.rxpol))
400

    
401
        if _mem.step > 0x05:
402
            _mem.step = 0x00
403
        mem.duplex = DUPLEX[_mem.duplex]
404
        mem.mode = _mem.isnarrow and "NFM" or "FM"
405
        mem.skip = "" if _mem.scanadd else "S"
406
        mem.power = POWER_LEVELS[_mem.highpower]
407

    
408
        if mem.freq == mem.offset and mem.duplex == "-":
409
            mem.duplex = "off"
410
            mem.offset = 0
411

    
412
        if _nam:
413
            for char in _nam:
414
                try:
415
                    mem.name += CHARSET[char]
416
                except IndexError:
417
                    break
418
            mem.name = mem.name.rstrip()
419

    
420
        mem.extra = RadioSettingGroup("Extra", "extra")
421

    
422
        rs = RadioSetting("bcl", "BCL",
423
                          RadioSettingValueBoolean(_mem.bcl))
424
        mem.extra.append(rs)
425

    
426
        rs = RadioSetting("revfreq", "Reverse Duplex",
427
                          RadioSettingValueBoolean(_mem.revfreq))
428
        mem.extra.append(rs)
429

    
430
        rs = RadioSetting("pttid", "PTT ID",
431
                          RadioSettingValueBoolean(_mem.pttid))
432
        mem.extra.append(rs)
433

    
434
        rs = RadioSetting("compander", "Compander",
435
                          RadioSettingValueBoolean(_mem.compander))
436
        mem.extra.append(rs)
437

    
438
        return mem
439

    
440
    def set_memory(self, mem):
441
        _mem, _nam = self._get_memobjs(mem.number)
442

    
443
        if mem.empty:
444
            if _nam is None:
445
                raise errors.InvalidValueError("VFO channels can not be empty")
446
            _mem.set_raw("\xFF" * 16)
447
            return
448

    
449
        if _mem.get_raw() == ("\xFF" * 16):
450
            _mem.set_raw("\x00" * 13 + "\xFF" * 3)
451

    
452
        _mem.freq = mem.freq / 10
453

    
454
        if mem.duplex == "off":
455
            _mem.duplex = DUPLEX.index("-")
456
            _mem.offset = _mem.freq
457
        elif mem.duplex == "split":
458
            diff = mem.offset - mem.freq
459
            _mem.duplex = DUPLEX.index("-") if diff < 0 else DUPLEX.index("+")
460
            _mem.offset = abs(diff) / 10
461
        else:
462
            _mem.offset = mem.offset / 10
463
            _mem.duplex = DUPLEX.index(mem.duplex)
464

    
465
        tx, rx = chirp_common.split_tone_encode(mem)
466
        self._encode_tone(_mem, 'tx', *tx)
467
        self._encode_tone(_mem, 'rx', *rx)
468

    
469
        _mem.isnarrow = mem.mode == "NFM"
470
        _mem.scanadd = mem.skip == ""
471
        _mem.highpower = mem.power == POWER_LEVELS[1]
472

    
473
        if _nam:
474
            for i in range(0, 5):
475
                try:
476
                    _nam[i] = CHARSET.index(mem.name[i])
477
                except IndexError:
478
                    _nam[i] = 0xFF
479

    
480
        for setting in mem.extra:
481
            setattr(_mem, setting.get_name(), setting.value)
482

    
483
    def get_settings(self):
484
        _settings = self._memobj.settings
485
        basic = RadioSettingGroup("basic", "Basic Settings")
486

    
487
        group = RadioSettings(basic)
488

    
489
        options = ["Time", "Carrier", "Search"]
490
        rs = RadioSetting("scantype", "Scan Type",
491
                          RadioSettingValueList(options,
492
                                                options[_settings.scantype]))
493
        basic.append(rs)
494

    
495
        options = ["Off"] + ["%s min" % x for x in range(1, 8)]
496
        rs = RadioSetting("timeout", "Time Out Timer",
497
                          RadioSettingValueList(
498
                              options, options[_settings.timeout]))
499
        basic.append(rs)
500

    
501
        options = ["A", "B"]
502
        rs = RadioSetting("freqmode_ab", "Frequency Mode",
503
                          RadioSettingValueList(
504
                              options, options[_settings.freqmode_ab]))
505
        basic.append(rs)
506

    
507
        options = ["Frequency Mode", "Channel Mode"]
508
        rs = RadioSetting("workmode_a", "Radio Work Mode(A)",
509
                          RadioSettingValueList(
510
                              options, options[_settings.workmode_a]))
511
        basic.append(rs)
512

    
513
        rs = RadioSetting("workmode_b", "Radio Work Mode(B)",
514
                          RadioSettingValueList(
515
                              options, options[_settings.workmode_b]))
516
        basic.append(rs)
517

    
518
        options = ["Frequency", "Name", "Channel"]
519
        rs = RadioSetting("mdf_a", "Display Format(F1)",
520
                          RadioSettingValueList(
521
                              options, options[_settings.mdf_a]))
522
        basic.append(rs)
523

    
524
        rs = RadioSetting("mdf_b", "Display Format(F2)",
525
                          RadioSettingValueList(
526
                              options, options[_settings.mdf_b]))
527
        basic.append(rs)
528

    
529
        rs = RadioSetting("mem_chan_a", "Mem Channel (A)",
530
                          RadioSettingValueInteger(
531
                              1, 99, _settings.mem_chan_a))
532
        basic.append(rs)
533

    
534
        rs = RadioSetting("mem_chan_b", "Mem Channel (B)",
535
                          RadioSettingValueInteger(
536
                              1, 99, _settings.mem_chan_b))
537
        basic.append(rs)
538

    
539
        options = ["Off", "BOT", "EOT", "Both"]
540
        rs = RadioSetting("pttid", "PTT-ID",
541
                          RadioSettingValueList(
542
                              options, options[_settings.pttid]))
543
        basic.append(rs)
544

    
545
        dtmfchars = "0123456789ABCD*#"
546
        _codeobj = self._memobj.pttid.code
547
        _code = "".join([dtmfchars[x] for x in _codeobj if int(x) < 0x1F])
548
        val = RadioSettingValueString(0, 6, _code, False)
549
        val.set_charset(dtmfchars)
550
        rs = RadioSetting("pttid.code", "PTT-ID Code", val)
551

    
552
        def apply_code(setting, obj):
553
            code = []
554
            for j in range(0, 6):
555
                try:
556
                    code.append(dtmfchars.index(str(setting.value)[j]))
557
                except IndexError:
558
                    code.append(0xFF)
559
            obj.code = code
560
        rs.set_apply_callback(apply_code, self._memobj.pttid)
561
        basic.append(rs)
562

    
563
        rs = RadioSetting("squelch", "Squelch Level",
564
                          RadioSettingValueInteger(0, 9, _settings.squelch))
565
        basic.append(rs)
566

    
567
        rs = RadioSetting("vox", "VOX Level",
568
                          RadioSettingValueInteger(0, 9, _settings.vox))
569
        basic.append(rs)
570

    
571
        options = ["Frequency Mode", "Channel Mode"]
572
        rs = RadioSetting("workmode_fm", "FM Work Mode",
573
                          RadioSettingValueList(
574
                              options, options[_settings.workmode_fm]))
575
        basic.append(rs)
576

    
577
        options = ["Current Frequency", "F1 Frequency", "F2 Frequency"]
578
        rs = RadioSetting("txtdr", "Dual Standby TX Priority",
579
                          RadioSettingValueList(options,
580
                                                options[_settings.txtdr]))
581
        basic.append(rs)
582

    
583
        options = ["English", "Chinese"]
584
        rs = RadioSetting("language", "Language",
585
                          RadioSettingValueList(options,
586
                                                options[_settings.language]))
587
        basic.append(rs)
588

    
589
        rs = RadioSetting("tdr", "Dual Standby",
590
                          RadioSettingValueBoolean(_settings.tdr))
591
        basic.append(rs)
592

    
593
        rs = RadioSetting("roger", "Roger Beep",
594
                          RadioSettingValueBoolean(_settings.roger))
595
        basic.append(rs)
596

    
597
        rs = RadioSetting("backlight", "Backlight",
598
                          RadioSettingValueBoolean(_settings.backlight))
599
        basic.append(rs)
600

    
601
        rs = RadioSetting("save_funct", "Save Mode",
602
                          RadioSettingValueBoolean(_settings.save_funct))
603
        basic.append(rs)
604

    
605
        rs = RadioSetting("fm", "FM Function",
606
                          RadioSettingValueBoolean(_settings.fm))
607
        basic.append(rs)
608

    
609
        rs = RadioSetting("beep_tone_disabled", "Beep Prompt",
610
                          RadioSettingValueBoolean(
611
                              not _settings.beep_tone_disabled))
612
        basic.append(rs)
613

    
614
        rs = RadioSetting("voice_prompt", "Voice Prompt",
615
                          RadioSettingValueBoolean(_settings.voice_prompt))
616
        basic.append(rs)
617

    
618
        rs = RadioSetting("sidetone", "DTMF Side Tone",
619
                          RadioSettingValueBoolean(_settings.sidetone))
620
        basic.append(rs)
621

    
622
        rs = RadioSetting("ste_disabled", "Squelch Tail Eliminate",
623
                          RadioSettingValueBoolean(not _settings.ste_disabled))
624
        basic.append(rs)
625

    
626
        _limit = int(self._memobj.limits.lower_vhf) / 10
627
        rs = RadioSetting("limits.lower_vhf", "VHF Lower Limit (MHz)",
628
                          RadioSettingValueInteger(128, 270, _limit))
629

    
630
        def apply_limit(setting, obj):
631
            value = int(setting.value) * 10
632
            obj.lower_vhf = value
633
        rs.set_apply_callback(apply_limit, self._memobj.limits)
634
        basic.append(rs)
635

    
636
        _limit = int(self._memobj.limits.upper_vhf) / 10
637
        rs = RadioSetting("limits.upper_vhf", "VHF Upper Limit (MHz)",
638
                          RadioSettingValueInteger(128, 270, _limit))
639

    
640
        def apply_limit(setting, obj):
641
            value = int(setting.value) * 10
642
            obj.upper_vhf = value
643
        rs.set_apply_callback(apply_limit, self._memobj.limits)
644
        basic.append(rs)
645

    
646
        _limit = int(self._memobj.limits.lower_uhf) / 10
647
        rs = RadioSetting("limits.lower_uhf", "UHF Lower Limit (MHz)",
648
                          RadioSettingValueInteger(400, 520, _limit))
649

    
650
        def apply_limit(setting, obj):
651
            value = int(setting.value) * 10
652
            obj.lower_uhf = value
653
        rs.set_apply_callback(apply_limit, self._memobj.limits)
654
        basic.append(rs)
655

    
656
        _limit = int(self._memobj.limits.upper_uhf) / 10
657
        rs = RadioSetting("limits.upper_uhf", "UHF Upper Limit (MHz)",
658
                          RadioSettingValueInteger(400, 520, _limit))
659

    
660
        def apply_limit(setting, obj):
661
            value = int(setting.value) * 10
662
            obj.upper_uhf = value
663
        rs.set_apply_callback(apply_limit, self._memobj.limits)
664
        basic.append(rs)
665

    
666
        fm_preset = RadioSettingGroup("fm_preset", "FM Radio Presets")
667
        group.append(fm_preset)
668

    
669
        for i in range(0, 16):
670
            if self._memobj.fm_presets[i] < 0x01AF:
671
                used = True
672
                preset = self._memobj.fm_presets[i] / 10.0 + 65
673
            else:
674
                used = False
675
                preset = 65
676
            rs = RadioSetting("fm_presets_%1i" % i, "FM Preset %i" % (i + 1),
677
                              RadioSettingValueBoolean(used),
678
                              RadioSettingValueFloat(65, 108, preset, 0.1, 1))
679
            fm_preset.append(rs)
680

    
681
        testmode = RadioSettingGroup("testmode", "Test Mode Settings")
682
        group.append(testmode)
683

    
684
        vhfdata = ["136-139", "140-144", "145-149", "150-154",
685
                   "155-159", "160-164", "165-169", "170-174"]
686
        uhfdata = ["400-409", "410-419", "420-429", "430-439",
687
                   "440-449", "450-459", "460-469", "470-479"]
688
        powernamedata = ["Hi", "Lo"]
689
        powerkeydata = ["hipwr", "lopwr"]
690

    
691
        for power in range(0, 2):
692
            for index in range(0, 8):
693
                key = "test.vhf%s%i" % (powerkeydata[power], index)
694
                name = "%s Mhz %s Power" % (vhfdata[index],
695
                                            powernamedata[power])
696
                rs = RadioSetting(
697
                        key, name, RadioSettingValueInteger(
698
                            0, 255, getattr(
699
                                self._memobj.test,
700
                                "vhf%s%i" % (powerkeydata[power], index))))
701
                testmode.append(rs)
702

    
703
        for power in range(0, 2):
704
            for index in range(0, 8):
705
                key = "test.uhf%s%i" % (powerkeydata[power], index)
706
                name = "%s Mhz %s Power" % (uhfdata[index],
707
                                            powernamedata[power])
708
                rs = RadioSetting(
709
                        key, name, RadioSettingValueInteger(
710
                            0, 255, getattr(
711
                                self._memobj.test,
712
                                "uhf%s%i" % (powerkeydata[power], index))))
713
                testmode.append(rs)
714

    
715
        for band in ["vhf", "uhf"]:
716
            for index in range(0, 10):
717
                key = "test.%ssquelch%i" % (band, index)
718
                name = "%s Squelch %i" % (band.upper(), index)
719
                rs = RadioSetting(
720
                        key, name, RadioSettingValueInteger(
721
                            0, 255, getattr(
722
                                self._memobj.test,
723
                                "%ssquelch%i" % (band, index))))
724
                testmode.append(rs)
725

    
726
        return group
727

    
728
    def set_settings(self, settings):
729
        _settings = self._memobj.settings
730
        for element in settings:
731
            if not isinstance(element, RadioSetting):
732
                if element.get_name() == "fm_preset":
733
                    self._set_fm_preset(element)
734
                else:
735
                    self.set_settings(element)
736
                    continue
737
            else:
738
                try:
739
                    name = element.get_name()
740
                    if "." in name:
741
                        bits = name.split(".")
742
                        obj = self._memobj
743
                        for bit in bits[:-1]:
744
                            if "/" in bit:
745
                                bit, index = bit.split("/", 1)
746
                                index = int(index)
747
                                obj = getattr(obj, bit)[index]
748
                            else:
749
                                obj = getattr(obj, bit)
750
                        setting = bits[-1]
751
                    else:
752
                        obj = _settings
753
                        setting = element.get_name()
754

    
755
                    if element.has_apply_callback():
756
                        LOG.debug("Using apply callback")
757
                        element.run_apply_callback()
758
                    elif setting == "beep_tone_disabled":
759
                        setattr(obj, setting, not int(element.value))
760
                    elif setting == "ste_disabled":
761
                        setattr(obj, setting, not int(element.value))
762
                    else:
763
                        LOG.debug("Setting %s = %s" % (setting, element.value))
764
                        setattr(obj, setting, element.value)
765
                except Exception, e:
766
                    LOG.debug(element.get_name())
767
                    raise
768

    
769
    def _set_fm_preset(self, settings):
770
        for element in settings:
771
            try:
772
                index = (int(element.get_name().split("_")[-1]))
773
                val = element.value
774
                if val[0].get_value():
775
                    value = int(val[1].get_value() * 10 - 650)
776
                else:
777
                    value = 0x01AF
778
                LOG.debug("Setting fm_presets[%1i] = %s" % (index, value))
779
                setting = self._memobj.fm_presets
780
                setting[index] = value
781
            except Exception, e:
782
                LOG.debug(element.get_name())
783
                raise
784

    
785
    @classmethod
786
    def match_model(cls, filedata, filename):
787
        return (filedata.startswith("KT511 Radio Program data") and
788
                len(filedata) == (cls._memsize + 0x30))
    (1-1/1)