baofeng_wp970i_uv-9g_dl_test_#1.py

Jim Unroe, 12/04/2021 06:01 pm

Download (30.3 kB)

 
1
# Copyright 2016:
2
# * Jim Unroe KC9HI, <rock.unroe@gmail.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 2 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
import time
18
import struct
19
import logging
20
import re
21

    
22
from chirp.drivers import baofeng_common
23
from chirp import chirp_common, directory, memmap
24
from chirp import bitwise, errors, util
25
from chirp.settings import RadioSettingGroup, RadioSetting, \
26
    RadioSettingValueBoolean, RadioSettingValueList, \
27
    RadioSettingValueString, RadioSettingValueInteger, \
28
    RadioSettingValueFloat, RadioSettings, \
29
    InvalidValueError
30
from textwrap import dedent
31

    
32
LOG = logging.getLogger(__name__)
33

    
34
# #### MAGICS #########################################################
35

    
36
# Baofeng WP970I magic string
37
MSTRING_WP970I = "\x50\xBB\xFF\x20\x14\x04\x13"
38

    
39
# Baofeng UV-9G magic string
40
MSTRING_UV9G = "\x50\xBB\xFF\x20\x12\x05\x25"
41

    
42

    
43
DTMF_CHARS = "0123456789 *#ABCD"
44
STEPS = [2.5, 5.0, 6.25, 10.0, 12.5, 20.0, 25.0, 50.0]
45

    
46
LIST_AB = ["A", "B"]
47
LIST_ALMOD = ["Site", "Tone", "Code"]
48
LIST_BANDWIDTH = ["Wide", "Narrow"]
49
LIST_COLOR = ["Off", "Blue", "Orange", "Purple"]
50
LIST_DTMFSPEED = ["%s ms" % x for x in range(50, 2010, 10)]
51
LIST_DTMFST = ["Off", "DT-ST", "ANI-ST", "DT+ANI"]
52
LIST_MODE = ["Channel", "Name", "Frequency"]
53
LIST_OFF1TO9 = ["Off"] + list("123456789")
54
LIST_OFF1TO10 = LIST_OFF1TO9 + ["10"]
55
LIST_OFFAB = ["Off"] + LIST_AB
56
LIST_RESUME = ["TO", "CO", "SE"]
57
LIST_PONMSG = ["Full", "Message"]
58
LIST_PTTID = ["Off", "BOT", "EOT", "Both"]
59
LIST_SCODE = ["%s" % x for x in range(1, 16)]
60
LIST_RPSTE = ["Off"] + ["%s" % x for x in range(1, 11)]
61
LIST_SAVE = ["Off", "1:1", "1:2", "1:3", "1:4"]
62
LIST_SHIFTD = ["Off", "+", "-"]
63
LIST_STEDELAY = ["Off"] + ["%s ms" % x for x in range(100, 1100, 100)]
64
LIST_STEP = [str(x) for x in STEPS]
65
LIST_TIMEOUT = ["%s sec" % x for x in range(15, 615, 15)]
66
LIST_TXPOWER = ["High", "Mid", "Low"]
67
LIST_VOICE = ["Off", "English", "Chinese"]
68
LIST_WORKMODE = ["Frequency", "Channel"]
69

    
70

    
71
def model_match(cls, data):
72
    """Match the opened/downloaded image to the correct version"""
73

    
74
    if len(data) > 0x2008:
75
        rid = data[0x2008:0x2010]
76
        return rid.startswith(cls.MODEL)
77
    elif len(data) == 0x2008:
78
        rid = data[0x1EF0:0x1EF7]
79
        return rid in cls._fileid
80
    else:
81
        return False
82

    
83

    
84
class WP970I(baofeng_common.BaofengCommonHT):
85
    """Baofeng WP970I"""
86
    VENDOR = "Baofeng"
87
    MODEL = "WP970I"
88

    
89
    _tri_band = False
90
    _fileid = []
91
    _magic = [MSTRING_WP970I, ]
92
    _magic_response_length = 8
93
    _fw_ver_start = 0x1EF0
94
    _recv_block_size = 0x40
95
    _mem_size = 0x2000
96
    _ack_block = True
97

    
98
    _ranges = [(0x0000, 0x0DF0),
99
               (0x0E00, 0x1800),
100
               (0x1EE0, 0x1EF0),
101
               (0x1F60, 0x1F70),
102
               (0x1F80, 0x1F90),
103
               (0x1FC0, 0x1FD0)]
104
    _send_block_size = 0x10
105

    
106
    MODES = ["NFM", "FM"]
107
    VALID_CHARS = chirp_common.CHARSET_ALPHANUMERIC + \
108
        "!@#$%^&*()+-=[]:\";'<>?,./"
109
    LENGTH_NAME = 6
110
    SKIP_VALUES = ["", "S"]
111
    DTCS_CODES = sorted(chirp_common.DTCS_CODES + [645])
112
    POWER_LEVELS = [chirp_common.PowerLevel("High", watts=5.00),
113
                    chirp_common.PowerLevel("Med",  watts=3.00),
114
                    chirp_common.PowerLevel("Low",  watts=1.00)]
115
    _vhf_range = (130000000, 180000000)
116
    _vhf2_range = (200000000, 260000000)
117
    _uhf_range = (400000000, 521000000)
118
    VALID_BANDS = [_vhf_range,
119
                   _uhf_range]
120
    PTTID_LIST = LIST_PTTID
121
    SCODE_LIST = LIST_SCODE
122

    
123
    MEM_FORMAT = """
124
    #seekto 0x0000;
125
    struct {
126
      lbcd rxfreq[4];
127
      lbcd txfreq[4];
128
      ul16 rxtone;
129
      ul16 txtone;
130
      u8 unused1:3,
131
         isuhf:1,
132
         scode:4;
133
      u8 unknown1:7,
134
         txtoneicon:1;
135
      u8 mailicon:3,
136
         unknown2:3,
137
         lowpower:2;
138
      u8 unknown3:1,
139
         wide:1,
140
         unknown4:2,
141
         bcl:1,
142
         scan:1,
143
         pttid:2;
144
    } memory[128];
145

    
146
    #seekto 0x0B00;
147
    struct {
148
      u8 code[5];
149
      u8 unused[11];
150
    } pttid[15];
151

    
152
    #seekto 0x0CAA;
153
    struct {
154
      u8 code[5];
155
      u8 unused1:6,
156
         aniid:2;
157
      u8 unknown[2];
158
      u8 dtmfon;
159
      u8 dtmfoff;
160
    } ani;
161

    
162
    #seekto 0x0E20;
163
    struct {
164
      u8 squelch;
165
      u8 step;
166
      u8 unknown1;
167
      u8 save;
168
      u8 vox;
169
      u8 unknown2;
170
      u8 abr;
171
      u8 tdr;
172
      u8 beep;
173
      u8 timeout;
174
      u8 unknown3[4];
175
      u8 voice;
176
      u8 unknown4;
177
      u8 dtmfst;
178
      u8 unknown5;
179
      u8 unknown12:6,
180
         screv:2;
181
      u8 pttid;
182
      u8 pttlt;
183
      u8 mdfa;
184
      u8 mdfb;
185
      u8 bcl;
186
      u8 autolk;
187
      u8 sftd;
188
      u8 unknown6[3];
189
      u8 wtled;
190
      u8 rxled;
191
      u8 txled;
192
      u8 almod;
193
      u8 band;
194
      u8 tdrab;
195
      u8 ste;
196
      u8 rpste;
197
      u8 rptrl;
198
      u8 ponmsg;
199
      u8 roger;
200
      u8 rogerrx;
201
      u8 tdrch;
202
      u8 displayab:1,
203
         unknown1:2,
204
         fmradio:1,
205
         alarm:1,
206
         unknown2:1,
207
         reset:1,
208
         menu:1;
209
      u8 unknown1:6,
210
         singleptt:1,
211
         vfomrlock:1;
212
      u8 workmode;
213
      u8 keylock;
214
    } settings;
215

    
216
    #seekto 0x0E76;
217
    struct {
218
      u8 unused1:1,
219
         mrcha:7;
220
      u8 unused2:1,
221
         mrchb:7;
222
    } wmchannel;
223

    
224
    struct vfo {
225
      u8 unknown0[8];
226
      u8 freq[8];
227
      u8 offset[6];
228
      ul16 rxtone;
229
      ul16 txtone;
230
      u8 unused1:7,
231
         band:1;
232
      u8 unknown3;
233
      u8 unused2:2,
234
         sftd:2,
235
         scode:4;
236
      u8 unknown4;
237
      u8 unused3:1
238
         step:3,
239
         unused4:4;
240
      u8 unused5:1,
241
         widenarr:1,
242
         unused6:4,
243
         txpower3:2;
244
    };
245

    
246
    #seekto 0x0F00;
247
    struct {
248
      struct vfo a;
249
      struct vfo b;
250
    } vfo;
251

    
252
    #seekto 0x0F4E;
253
    u16 fm_presets;
254

    
255
    #seekto 0x1000;
256
    struct {
257
      char name[7];
258
      u8 unknown1[9];
259
    } names[128];
260

    
261
    #seekto 0x1ED0;
262
    struct {
263
      char line1[7];
264
      char line2[7];
265
    } sixpoweron_msg;
266

    
267
    #seekto 0x1EE0;
268
    struct {
269
      char line1[7];
270
      char line2[7];
271
    } poweron_msg;
272

    
273
    #seekto 0x1EF0;
274
    struct {
275
      char line1[7];
276
      char line2[7];
277
    } firmware_msg;
278

    
279
    struct squelch {
280
      u8 sql0;
281
      u8 sql1;
282
      u8 sql2;
283
      u8 sql3;
284
      u8 sql4;
285
      u8 sql5;
286
      u8 sql6;
287
      u8 sql7;
288
      u8 sql8;
289
      u8 sql9;
290
    };
291

    
292
    #seekto 0x1F60;
293
    struct {
294
      struct squelch vhf;
295
      u8 unknown1[6];
296
      u8 unknown2[16];
297
      struct squelch uhf;
298
    } squelch;
299

    
300
    struct limit {
301
      u8 enable;
302
      bbcd lower[2];
303
      bbcd upper[2];
304
    };
305

    
306
    #seekto 0x1FC0;
307
    struct {
308
      struct limit vhf;
309
      struct limit uhf;
310
      struct limit vhf2;
311
    } limits;
312

    
313
    """
314

    
315
    @classmethod
316
    def get_prompts(cls):
317
        rp = chirp_common.RadioPrompts()
318
        rp.experimental = \
319
            ('This driver is a beta version.\n'
320
             '\n'
321
             'Please save an unedited copy of your first successful\n'
322
             'download to a CHIRP Radio Images(*.img) file.'
323
             )
324
        rp.pre_download = _(dedent("""\
325
            Follow these instructions to download your info:
326

    
327
            1 - Turn off your radio
328
            2 - Connect your interface cable
329
            3 - Turn on your radio
330
            4 - Do the download of your radio data
331
            """))
332
        rp.pre_upload = _(dedent("""\
333
            Follow this instructions to upload your info:
334

    
335
            1 - Turn off your radio
336
            2 - Connect your interface cable
337
            3 - Turn on your radio
338
            4 - Do the upload of your radio data
339
            """))
340
        return rp
341

    
342
    def get_features(self):
343
        rf = baofeng_common.BaofengCommonHT.get_features(self)
344
        rf.valid_tuning_steps = STEPS
345
        return rf
346

    
347
    def process_mmap(self):
348
        """Process the mem map into the mem object"""
349
        self._memobj = bitwise.parse(self.MEM_FORMAT, self._mmap)
350

    
351
    def get_settings(self):
352
        """Translate the bit in the mem_struct into settings in the UI"""
353
        _mem = self._memobj
354
        basic = RadioSettingGroup("basic", "Basic Settings")
355
        advanced = RadioSettingGroup("advanced", "Advanced Settings")
356
        other = RadioSettingGroup("other", "Other Settings")
357
        work = RadioSettingGroup("work", "Work Mode Settings")
358
        fm_preset = RadioSettingGroup("fm_preset", "FM Preset")
359
        dtmfe = RadioSettingGroup("dtmfe", "DTMF Encode Settings")
360
        service = RadioSettingGroup("service", "Service Settings")
361
        top = RadioSettings(basic, advanced, other, work, fm_preset, dtmfe,
362
                            service)
363

    
364
        # Basic settings
365
        if _mem.settings.squelch > 0x09:
366
            val = 0x00
367
        else:
368
            val = _mem.settings.squelch
369
        rs = RadioSetting("settings.squelch", "Squelch",
370
                          RadioSettingValueList(
371
                              LIST_OFF1TO9, LIST_OFF1TO9[val]))
372
        basic.append(rs)
373

    
374
        if _mem.settings.save > 0x04:
375
            val = 0x00
376
        else:
377
            val = _mem.settings.save
378
        rs = RadioSetting("settings.save", "Battery Saver",
379
                          RadioSettingValueList(
380
                              LIST_SAVE, LIST_SAVE[val]))
381
        basic.append(rs)
382

    
383
        if _mem.settings.vox > 0x0A:
384
            val = 0x00
385
        else:
386
            val = _mem.settings.vox
387
        rs = RadioSetting("settings.vox", "Vox",
388
                          RadioSettingValueList(
389
                              LIST_OFF1TO10, LIST_OFF1TO10[val]))
390
        basic.append(rs)
391

    
392
        if _mem.settings.abr > 0x0A:
393
            val = 0x00
394
        else:
395
            val = _mem.settings.abr
396
        rs = RadioSetting("settings.abr", "Backlight Timeout",
397
                          RadioSettingValueList(
398
                              LIST_OFF1TO10, LIST_OFF1TO10[val]))
399
        basic.append(rs)
400

    
401
        rs = RadioSetting("settings.tdr", "Dual Watch",
402
                          RadioSettingValueBoolean(_mem.settings.tdr))
403
        basic.append(rs)
404

    
405
        rs = RadioSetting("settings.beep", "Beep",
406
                          RadioSettingValueBoolean(_mem.settings.beep))
407
        basic.append(rs)
408

    
409
        if _mem.settings.timeout > 0x27:
410
            val = 0x03
411
        else:
412
            val = _mem.settings.timeout
413
        rs = RadioSetting("settings.timeout", "Timeout Timer",
414
                          RadioSettingValueList(
415
                              LIST_TIMEOUT, LIST_TIMEOUT[val]))
416
        basic.append(rs)
417

    
418
        if _mem.settings.voice > 0x02:
419
            val = 0x01
420
        else:
421
            val = _mem.settings.voice
422
        rs = RadioSetting("settings.voice", "Voice Prompt",
423
                          RadioSettingValueList(
424
                              LIST_VOICE, LIST_VOICE[val]))
425
        basic.append(rs)
426

    
427
        rs = RadioSetting("settings.dtmfst", "DTMF Sidetone",
428
                          RadioSettingValueList(LIST_DTMFST, LIST_DTMFST[
429
                              _mem.settings.dtmfst]))
430
        basic.append(rs)
431

    
432
        if _mem.settings.screv > 0x02:
433
            val = 0x01
434
        else:
435
            val = _mem.settings.screv
436
        rs = RadioSetting("settings.screv", "Scan Resume",
437
                          RadioSettingValueList(
438
                              LIST_RESUME, LIST_RESUME[val]))
439
        basic.append(rs)
440

    
441
        rs = RadioSetting("settings.pttid", "When to send PTT ID",
442
                          RadioSettingValueList(LIST_PTTID, LIST_PTTID[
443
                              _mem.settings.pttid]))
444
        basic.append(rs)
445

    
446
        if _mem.settings.pttlt > 0x1E:
447
            val = 0x05
448
        else:
449
            val = _mem.settings.pttlt
450
        rs = RadioSetting("pttlt", "PTT ID Delay",
451
                          RadioSettingValueInteger(0, 50, val))
452
        basic.append(rs)
453

    
454
        rs = RadioSetting("settings.mdfa", "Display Mode (A)",
455
                          RadioSettingValueList(LIST_MODE, LIST_MODE[
456
                              _mem.settings.mdfa]))
457
        basic.append(rs)
458

    
459
        rs = RadioSetting("settings.mdfb", "Display Mode (B)",
460
                          RadioSettingValueList(LIST_MODE, LIST_MODE[
461
                              _mem.settings.mdfb]))
462
        basic.append(rs)
463

    
464
        rs = RadioSetting("settings.autolk", "Automatic Key Lock",
465
                          RadioSettingValueBoolean(_mem.settings.autolk))
466
        basic.append(rs)
467

    
468
        rs = RadioSetting("settings.wtled", "Standby LED Color",
469
                          RadioSettingValueList(
470
                              LIST_COLOR, LIST_COLOR[_mem.settings.wtled]))
471
        basic.append(rs)
472

    
473
        rs = RadioSetting("settings.rxled", "RX LED Color",
474
                          RadioSettingValueList(
475
                              LIST_COLOR, LIST_COLOR[_mem.settings.rxled]))
476
        basic.append(rs)
477

    
478
        rs = RadioSetting("settings.txled", "TX LED Color",
479
                          RadioSettingValueList(
480
                              LIST_COLOR, LIST_COLOR[_mem.settings.txled]))
481
        basic.append(rs)
482

    
483
        val = _mem.settings.almod
484
        rs = RadioSetting("settings.almod", "Alarm Mode",
485
                          RadioSettingValueList(
486
                              LIST_ALMOD, LIST_ALMOD[val]))
487
        basic.append(rs)
488

    
489
        if _mem.settings.tdrab > 0x02:
490
            val = 0x00
491
        else:
492
            val = _mem.settings.tdrab
493
        rs = RadioSetting("settings.tdrab", "Dual Watch TX Priority",
494
                          RadioSettingValueList(
495
                              LIST_OFFAB, LIST_OFFAB[val]))
496
        basic.append(rs)
497

    
498
        rs = RadioSetting("settings.ste", "Squelch Tail Eliminate (HT to HT)",
499
                          RadioSettingValueBoolean(_mem.settings.ste))
500
        basic.append(rs)
501

    
502
        if _mem.settings.rpste > 0x0A:
503
            val = 0x00
504
        else:
505
            val = _mem.settings.rpste
506
        rs = RadioSetting("settings.rpste",
507
                          "Squelch Tail Eliminate (repeater)",
508
                          RadioSettingValueList(
509
                              LIST_RPSTE, LIST_RPSTE[val]))
510
        basic.append(rs)
511

    
512
        if _mem.settings.rptrl > 0x0A:
513
            val = 0x00
514
        else:
515
            val = _mem.settings.rptrl
516
        rs = RadioSetting("settings.rptrl", "STE Repeater Delay",
517
                          RadioSettingValueList(
518
                              LIST_STEDELAY, LIST_STEDELAY[val]))
519
        basic.append(rs)
520

    
521
        rs = RadioSetting("settings.ponmsg", "Power-On Message",
522
                          RadioSettingValueList(LIST_PONMSG, LIST_PONMSG[
523
                              _mem.settings.ponmsg]))
524
        basic.append(rs)
525

    
526
        rs = RadioSetting("settings.roger", "Roger Beep",
527
                          RadioSettingValueBoolean(_mem.settings.roger))
528
        basic.append(rs)
529

    
530
        # Advanced settings
531
        rs = RadioSetting("settings.reset", "RESET Menu",
532
                          RadioSettingValueBoolean(_mem.settings.reset))
533
        advanced.append(rs)
534

    
535
        rs = RadioSetting("settings.menu", "All Menus",
536
                          RadioSettingValueBoolean(_mem.settings.menu))
537
        advanced.append(rs)
538

    
539
        rs = RadioSetting("settings.fmradio", "Broadcast FM Radio",
540
                          RadioSettingValueBoolean(_mem.settings.fmradio))
541
        advanced.append(rs)
542

    
543
        rs = RadioSetting("settings.alarm", "Alarm Sound",
544
                          RadioSettingValueBoolean(_mem.settings.alarm))
545
        advanced.append(rs)
546

    
547
        # Other settings
548
        def _filter(name):
549
            filtered = ""
550
            for char in str(name):
551
                if char in chirp_common.CHARSET_ASCII:
552
                    filtered += char
553
                else:
554
                    filtered += " "
555
            return filtered
556

    
557
        _msg = _mem.firmware_msg
558
        val = RadioSettingValueString(0, 7, _filter(_msg.line1))
559
        val.set_mutable(False)
560
        rs = RadioSetting("firmware_msg.line1", "Firmware Message 1", val)
561
        other.append(rs)
562

    
563
        val = RadioSettingValueString(0, 7, _filter(_msg.line2))
564
        val.set_mutable(False)
565
        rs = RadioSetting("firmware_msg.line2", "Firmware Message 2", val)
566
        other.append(rs)
567

    
568
        _msg = _mem.sixpoweron_msg
569
        val = RadioSettingValueString(0, 7, _filter(_msg.line1))
570
        val.set_mutable(False)
571
        rs = RadioSetting("sixpoweron_msg.line1", "6+Power-On Message 1", val)
572
        other.append(rs)
573
        val = RadioSettingValueString(0, 7, _filter(_msg.line2))
574
        val.set_mutable(False)
575
        rs = RadioSetting("sixpoweron_msg.line2", "6+Power-On Message 2", val)
576
        other.append(rs)
577

    
578
        _msg = _mem.poweron_msg
579
        rs = RadioSetting("poweron_msg.line1", "Power-On Message 1",
580
                          RadioSettingValueString(
581
                              0, 7, _filter(_msg.line1)))
582
        other.append(rs)
583
        rs = RadioSetting("poweron_msg.line2", "Power-On Message 2",
584
                          RadioSettingValueString(
585
                              0, 7, _filter(_msg.line2)))
586
        other.append(rs)
587

    
588
        lower = 130
589
        upper = 179
590
        rs = RadioSetting("limits.vhf.lower", "VHF Lower Limit (MHz)",
591
                          RadioSettingValueInteger(
592
                              lower, upper, _mem.limits.vhf.lower))
593
        other.append(rs)
594

    
595
        rs = RadioSetting("limits.vhf.upper", "VHF Upper Limit (MHz)",
596
                          RadioSettingValueInteger(
597
                              lower, upper, _mem.limits.vhf.upper))
598
        other.append(rs)
599

    
600
        if self._tri_band:
601
            lower = 200
602
            upper = 260
603
            rs = RadioSetting("limits.vhf2.lower", "VHF2 Lower Limit (MHz)",
604
                              RadioSettingValueInteger(
605
                                  lower, upper, _mem.limits.vhf2.lower))
606
            other.append(rs)
607

    
608
            rs = RadioSetting("limits.vhf2.upper", "VHF2 Upper Limit (MHz)",
609
                              RadioSettingValueInteger(
610
                                  lower, upper, _mem.limits.vhf2.upper))
611
            other.append(rs)
612

    
613
        lower = 400
614
        upper = 520
615
        rs = RadioSetting("limits.uhf.lower", "UHF Lower Limit (MHz)",
616
                          RadioSettingValueInteger(
617
                              lower, upper, _mem.limits.uhf.lower))
618
        other.append(rs)
619

    
620
        rs = RadioSetting("limits.uhf.upper", "UHF Upper Limit (MHz)",
621
                          RadioSettingValueInteger(
622
                              lower, upper, _mem.limits.uhf.upper))
623
        other.append(rs)
624

    
625
        # Work mode settings
626
        rs = RadioSetting("settings.displayab", "Display",
627
                          RadioSettingValueList(
628
                              LIST_AB, LIST_AB[_mem.settings.displayab]))
629
        work.append(rs)
630

    
631
        rs = RadioSetting("settings.workmode", "VFO/MR Mode",
632
                          RadioSettingValueList(
633
                              LIST_WORKMODE,
634
                              LIST_WORKMODE[_mem.settings.workmode]))
635
        work.append(rs)
636

    
637
        rs = RadioSetting("settings.keylock", "Keypad Lock",
638
                          RadioSettingValueBoolean(_mem.settings.keylock))
639
        work.append(rs)
640

    
641
        rs = RadioSetting("wmchannel.mrcha", "MR A Channel",
642
                          RadioSettingValueInteger(0, 127,
643
                                                   _mem.wmchannel.mrcha))
644
        work.append(rs)
645

    
646
        rs = RadioSetting("wmchannel.mrchb", "MR B Channel",
647
                          RadioSettingValueInteger(0, 127,
648
                                                   _mem.wmchannel.mrchb))
649
        work.append(rs)
650

    
651
        def convert_bytes_to_freq(bytes):
652
            real_freq = 0
653
            for byte in bytes:
654
                real_freq = (real_freq * 10) + byte
655
            return chirp_common.format_freq(real_freq * 10)
656

    
657
        def my_validate(value):
658
            value = chirp_common.parse_freq(value)
659
            msg = ("Can't be less than %i.0000")
660
            if value > 99000000 and value < 130 * 1000000:
661
                raise InvalidValueError(msg % (130))
662
            msg = ("Can't be between %i.9975-%i.0000")
663
            if (179 + 1) * 1000000 <= value and value < 400 * 1000000:
664
                raise InvalidValueError(msg % (179, 400))
665
            msg = ("Can't be greater than %i.9975")
666
            if value > 99000000 and value > (520 + 1) * 1000000:
667
                raise InvalidValueError(msg % (520))
668
            return chirp_common.format_freq(value)
669

    
670
        def apply_freq(setting, obj):
671
            value = chirp_common.parse_freq(str(setting.value)) / 10
672
            for i in range(7, -1, -1):
673
                obj.freq[i] = value % 10
674
                value /= 10
675

    
676
        val1a = RadioSettingValueString(0, 10,
677
                                        convert_bytes_to_freq(_mem.vfo.a.freq))
678
        val1a.set_validate_callback(my_validate)
679
        rs = RadioSetting("vfo.a.freq", "VFO A Frequency", val1a)
680
        rs.set_apply_callback(apply_freq, _mem.vfo.a)
681
        work.append(rs)
682

    
683
        val1b = RadioSettingValueString(0, 10,
684
                                        convert_bytes_to_freq(_mem.vfo.b.freq))
685
        val1b.set_validate_callback(my_validate)
686
        rs = RadioSetting("vfo.b.freq", "VFO B Frequency", val1b)
687
        rs.set_apply_callback(apply_freq, _mem.vfo.b)
688
        work.append(rs)
689

    
690
        rs = RadioSetting("vfo.a.sftd", "VFO A Shift",
691
                          RadioSettingValueList(
692
                              LIST_SHIFTD, LIST_SHIFTD[_mem.vfo.a.sftd]))
693
        work.append(rs)
694

    
695
        rs = RadioSetting("vfo.b.sftd", "VFO B Shift",
696
                          RadioSettingValueList(
697
                              LIST_SHIFTD, LIST_SHIFTD[_mem.vfo.b.sftd]))
698
        work.append(rs)
699

    
700
        def convert_bytes_to_offset(bytes):
701
            real_offset = 0
702
            for byte in bytes:
703
                real_offset = (real_offset * 10) + byte
704
            return chirp_common.format_freq(real_offset * 1000)
705

    
706
        def apply_offset(setting, obj):
707
            value = chirp_common.parse_freq(str(setting.value)) / 1000
708
            for i in range(5, -1, -1):
709
                obj.offset[i] = value % 10
710
                value /= 10
711

    
712
        val1a = RadioSettingValueString(
713
                    0, 10, convert_bytes_to_offset(_mem.vfo.a.offset))
714
        rs = RadioSetting("vfo.a.offset",
715
                          "VFO A Offset", val1a)
716
        rs.set_apply_callback(apply_offset, _mem.vfo.a)
717
        work.append(rs)
718

    
719
        val1b = RadioSettingValueString(
720
                    0, 10, convert_bytes_to_offset(_mem.vfo.b.offset))
721
        rs = RadioSetting("vfo.b.offset",
722
                          "VFO B Offset", val1b)
723
        rs.set_apply_callback(apply_offset, _mem.vfo.b)
724
        work.append(rs)
725

    
726
        rs = RadioSetting("vfo.a.txpower3", "VFO A Power",
727
                          RadioSettingValueList(
728
                              LIST_TXPOWER,
729
                              LIST_TXPOWER[_mem.vfo.a.txpower3]))
730
        work.append(rs)
731

    
732
        rs = RadioSetting("vfo.b.txpower3", "VFO B Power",
733
                          RadioSettingValueList(
734
                              LIST_TXPOWER,
735
                              LIST_TXPOWER[_mem.vfo.b.txpower3]))
736
        work.append(rs)
737

    
738
        rs = RadioSetting("vfo.a.widenarr", "VFO A Bandwidth",
739
                          RadioSettingValueList(
740
                              LIST_BANDWIDTH,
741
                              LIST_BANDWIDTH[_mem.vfo.a.widenarr]))
742
        work.append(rs)
743

    
744
        rs = RadioSetting("vfo.b.widenarr", "VFO B Bandwidth",
745
                          RadioSettingValueList(
746
                              LIST_BANDWIDTH,
747
                              LIST_BANDWIDTH[_mem.vfo.b.widenarr]))
748
        work.append(rs)
749

    
750
        rs = RadioSetting("vfo.a.scode", "VFO A S-CODE",
751
                          RadioSettingValueList(
752
                              LIST_SCODE,
753
                              LIST_SCODE[_mem.vfo.a.scode]))
754
        work.append(rs)
755

    
756
        rs = RadioSetting("vfo.b.scode", "VFO B S-CODE",
757
                          RadioSettingValueList(
758
                              LIST_SCODE,
759
                              LIST_SCODE[_mem.vfo.b.scode]))
760
        work.append(rs)
761

    
762
        rs = RadioSetting("vfo.a.step", "VFO A Tuning Step",
763
                          RadioSettingValueList(
764
                              LIST_STEP, LIST_STEP[_mem.vfo.a.step]))
765
        work.append(rs)
766
        rs = RadioSetting("vfo.b.step", "VFO B Tuning Step",
767
                          RadioSettingValueList(
768
                              LIST_STEP, LIST_STEP[_mem.vfo.b.step]))
769
        work.append(rs)
770

    
771
        # broadcast FM settings
772
        _fm_presets = self._memobj.fm_presets
773
        if _fm_presets <= 108.0 * 10 - 650:
774
            preset = _fm_presets / 10.0 + 65
775
        elif _fm_presets >= 65.0 * 10 and _fm_presets <= 108.0 * 10:
776
            preset = _fm_presets / 10.0
777
        else:
778
            preset = 76.0
779
        rs = RadioSetting("fm_presets", "FM Preset(MHz)",
780
                          RadioSettingValueFloat(65, 108.0, preset, 0.1, 1))
781
        fm_preset.append(rs)
782

    
783
        # DTMF settings
784
        def apply_code(setting, obj, length):
785
            code = []
786
            for j in range(0, length):
787
                try:
788
                    code.append(DTMF_CHARS.index(str(setting.value)[j]))
789
                except IndexError:
790
                    code.append(0xFF)
791
            obj.code = code
792

    
793
        for i in range(0, 15):
794
            _codeobj = self._memobj.pttid[i].code
795
            _code = "".join([DTMF_CHARS[x] for x in _codeobj if int(x) < 0x1F])
796
            val = RadioSettingValueString(0, 5, _code, False)
797
            val.set_charset(DTMF_CHARS)
798
            pttid = RadioSetting("pttid/%i.code" % i,
799
                                 "Signal Code %i" % (i + 1), val)
800
            pttid.set_apply_callback(apply_code, self._memobj.pttid[i], 5)
801
            dtmfe.append(pttid)
802

    
803
        if _mem.ani.dtmfon > 0xC3:
804
            val = 0x03
805
        else:
806
            val = _mem.ani.dtmfon
807
        rs = RadioSetting("ani.dtmfon", "DTMF Speed (on)",
808
                          RadioSettingValueList(LIST_DTMFSPEED,
809
                                                LIST_DTMFSPEED[val]))
810
        dtmfe.append(rs)
811

    
812
        if _mem.ani.dtmfoff > 0xC3:
813
            val = 0x03
814
        else:
815
            val = _mem.ani.dtmfoff
816
        rs = RadioSetting("ani.dtmfoff", "DTMF Speed (off)",
817
                          RadioSettingValueList(LIST_DTMFSPEED,
818
                                                LIST_DTMFSPEED[val]))
819
        dtmfe.append(rs)
820

    
821
        _codeobj = self._memobj.ani.code
822
        _code = "".join([DTMF_CHARS[x] for x in _codeobj if int(x) < 0x1F])
823
        val = RadioSettingValueString(0, 5, _code, False)
824
        val.set_charset(DTMF_CHARS)
825
        rs = RadioSetting("ani.code", "ANI Code", val)
826
        rs.set_apply_callback(apply_code, self._memobj.ani, 5)
827
        dtmfe.append(rs)
828

    
829
        rs = RadioSetting("ani.aniid", "When to send ANI ID",
830
                          RadioSettingValueList(LIST_PTTID,
831
                                                LIST_PTTID[_mem.ani.aniid]))
832
        dtmfe.append(rs)
833

    
834
        # Service settings
835
        for band in ["vhf", "uhf"]:
836
            for index in range(0, 10):
837
                key = "squelch.%s.sql%i" % (band, index)
838
                if band == "vhf":
839
                    _obj = self._memobj.squelch.vhf
840
                elif band == "uhf":
841
                    _obj = self._memobj.squelch.uhf
842
                val = RadioSettingValueInteger(0, 123,
843
                                               getattr(
844
                                                   _obj, "sql%i" % (index)))
845
                if index == 0:
846
                    val.set_mutable(False)
847
                name = "%s Squelch %i" % (band.upper(), index)
848
                rs = RadioSetting(key, name, val)
849
                service.append(rs)
850

    
851
        return top
852

    
853
    @classmethod
854
    def match_model(cls, filedata, filename):
855
        match_size = False
856
        match_model = False
857

    
858
        # testing the file data size
859
        if len(filedata) in [0x2008, 0x2010]:
860
            match_size = True
861

    
862
        # testing the firmware model fingerprint
863
        match_model = model_match(cls, filedata)
864

    
865
        if match_size and match_model:
866
            return True
867
        else:
868
            return False
869

    
870

    
871
class RH5XAlias(chirp_common.Alias):
872
    VENDOR = "Rugged"
873
    MODEL = "RH5X"
874

    
875

    
876
class UV82IIIAlias(chirp_common.Alias):
877
    VENDOR = "Baofeng"
878
    MODEL = "UV-82III"
879

    
880

    
881
@directory.register
882
class BFA58(WP970I):
883
    """Baofeng BF-A58"""
884
    VENDOR = "Baofeng"
885
    MODEL = "BF-A58"
886
    ALIASES = [RH5XAlias]
887

    
888
    _fileid = ["BFT515 ", "BFT517 "]
889

    
890

    
891
@directory.register
892
class UV82WP(WP970I):
893
    """Baofeng UV82-WP"""
894
    VENDOR = "Baofeng"
895
    MODEL = "UV-82WP"
896

    
897

    
898
@directory.register
899
class GT3WP(WP970I):
900
    """Baofeng GT-3WP"""
901
    VENDOR = "Baofeng"
902
    MODEL = "GT-3WP"
903
    LENGTH_NAME = 7
904

    
905

    
906
@directory.register
907
class RT6(WP970I):
908
    """Retevis RT6"""
909
    VENDOR = "Retevis"
910
    MODEL = "RT6"
911

    
912

    
913
@directory.register
914
class BFA58S(WP970I):
915
    VENDOR = "Baofeng"
916
    MODEL = "BF-A58S"
917
    LENGTH_NAME = 7
918
    ALIASES = [UV82IIIAlias]
919
    _tri_band = True
920

    
921
    def get_features(self):
922
        rf = WP970I.get_features(self)
923
        rf.valid_bands = [self._vhf_range,
924
                          self._vhf2_range,
925
                          self._uhf_range]
926
        return rf
927

    
928

    
929
@directory.register
930
class UV9R(WP970I):
931
    """Baofeng UV-9R"""
932
    VENDOR = "Baofeng"
933
    MODEL = "UV-9R"
934
    LENGTH_NAME = 7
935

    
936

    
937
@directory.register
938
class UV9R(WP970I):
939
    """Baofeng UV-9G"""
940
    VENDOR = "Baofeng"
941
    MODEL = "UV-9G"
942
    LENGTH_NAME = 7
943

    
944
    _magic = [MSTRING_UV9G, ]
945

    
946
    
947
    def sync_out(self):
948
        """Upload to radio"""
949
        try:
950
            _upload(self)
951
        except errors.RadioError:
952
            raise
953
        except Exception, e:
954
            # If anything unexpected happens, make sure we raise
955
            # a RadioError and log the problem
956
            LOG.exception('Unexpected error during upload')
957
            raise errors.RadioError('Unexpected error communicating '
958
                                    'with the radio')
959

    
960
    def get_features(self):
961
        rf = WP970I.get_features(self)
962
        rf.has_settings = False
963
        return rf
964

    
965
    @classmethod
966
    def match_model(cls, filedata, filename):
967
        # This radio has always been post-metadata, so never do
968
        # old-school detection
969
        return False
970