Project

General

Profile

New Model #1035 » th9000uhf.py

for UHF version of radio - mike 240, 03/10/2015 06:33 PM

 
1
# Copyright 2012 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 os
17
import struct
18
import time
19

    
20
from chirp import chirp_common
21
from chirp import directory
22
from chirp import memmap
23
from chirp import bitwise
24
from chirp import errors
25
from chirp import util
26
from chirp.settings import RadioSetting, RadioSettingGroup, \
27
        RadioSettingValueInteger, RadioSettingValueList, \
28
        RadioSettingValueBoolean, RadioSettingValueString, \
29
        RadioSettingValueFloat, InvalidValueError
30

    
31
#
32
#  Chirp Driver for TYT TH-9000 UHF (70cm) Model
33
#  by David Fannin <dfannin@sushisoft.com>, KK6DF
34
#
35
#  Version 0.4 (Experimental - Known Bugs and Issues)
36
#  Use for development purposes only!  
37
#  Features working:
38
#         - Download from Radio
39
#         - Display Memories (only None, Tone, TSQL signalling supported)
40
#         - Save image file
41
#         - memory map decoded (about 90%)
42
#         - Upload to radio
43
#         - Modification of memories
44
#         - feature settings
45
#         - added Startup ID label
46
#
47
#  Features not working:
48
#         - DCS , Cross Signaling
49
#         - Skip channels
50

    
51

    
52
#
53
# Global Parameters 
54
#
55
MMAPSIZE = 16128
56
TONES = [62.5] + list(chirp_common.TONES)
57
TMODES =  ['','Tone','DTCS'] 
58
DUPLEXES = ['','err','-','+'] # index 2 not used
59
MODES = ['WFM','FM','NFM']  #  25k, 20k,15k bw 
60
TUNING_STEPS=[ 5.0, 6.25, 8.33, 10.0, 12.5, 15.0, 20.0, 25.0, 30.0, 50.0 ] # index 0-9
61
POWER_LEVELS=[chirp_common.PowerLevel("High", watts=65),
62
              chirp_common.PowerLevel("Mid", watts=25),
63
              chirp_common.PowerLevel("Low", watts=10)]
64

    
65
CROSS_MODES = chirp_common.CROSS_MODES
66
VALID_MODEL = ['TH-9000']
67

    
68
APO_LIST = [ "Off","30 min","1 hr","2 hrs" ] 
69
BGCOLOR_LIST = ["Blue","Orange","Purple"]
70
BGBRIGHT_LIST = ["%s" % x for x in range(1,32)]
71
SQUELCH_LIST = ["Off"] + ["Level %s" % x for x in range(1,20)] 
72
TIMEOUT_LIST = ["Off"] + ["%s min" % x for x in range(1,30)]
73
TXPWR_LIST = ["60W","25W"]  # maximum power for Hi setting
74
TBSTFREQ_LIST = ["1750Hz","2100Hz","1000Hz","1450Hz"]
75
BEEP_LIST = ["Off","On"]
76

    
77
SETTING_LISTS = {
78
        "auto_power_off": APO_LIST,
79
        "bg_color"      : BGCOLOR_LIST,
80
        "bg_brightness" : BGBRIGHT_LIST,
81
        "squelch"       : SQUELCH_LIST,
82
        "timeout_timer" : TIMEOUT_LIST,
83
        "choose_tx_power": TXPWR_LIST,
84
        "tbst_freq"     : TBSTFREQ_LIST,
85
        "voice_prompt"  : BEEP_LIST
86
}
87

    
88

    
89
#
90
#
91
#
92
"""
93
Overall Memory Map:
94

    
95
    Memory Map (Range 0x0100-3FF0, step 0x10):
96

    
97
        Field                   Start  End  Size   
98
                                (hex)  (hex) (hex)  
99
        
100
        1 Channel Set Flag        0100  011F   20 
101
        2 Channel Skip Flag       0120  013F   20 
102
        3 Blank/Unknown           0140  01EF   B0 
103
        4 Unknown                 01F0  01FF   10
104
        5 TX/RX Range             0200  020F   10    
105
        6 Bootup Passwd           0210  021F   10 
106
        7 Options, Radio          0220  023F   20
107
        8 Unknown                 0240  019F   
108
            8B Startup Label      03E0  03E7   07
109
        9 Channel Bank            2000  38FF 1900  
110
             Channel 000          2000  201F   20  
111
             Channel 001          2020  202F   20  
112
             ... 
113
             Channel 199          38E0  38FF   20 
114
        10 Blank/Unknown          3900  3FFF  6FF  14592  16383    1792   
115
            Total Map Size           16128 (2^8 = 16384)
116

    
117

    
118
"""
119

    
120
"""
121
  TH9000/UHF  memory map 
122
  section: 1 and 2:  Channel Set/Skip Flags
123

    
124
    Channel Set (starts 0x100) : Channel Set  bit is value 0 if a memory location in the channel bank is active.
125
    Channel Skip (starts 0x120): Channel Skip bit is value 0 if a memory location in the channel bank is active.
126

    
127
    Both flag maps are a total 24 bytes in length, aligned on 32 byte records.
128
    bit = 0 channel set/no skip,  1 is channel not set/skip
129

    
130
    to index a channel:
131
        cbyte = channel / 8 ;
132
        cbit  = channel % 8 ;
133
        setflag  = csetflag[cbyte].c[cbit] ;
134
        skipflag = cskipflag[cbyte].c[cbit] ;
135

    
136
    channel range is 0-199, range is 32 bytes (last 7 unknown)
137
"""
138

    
139

    
140
MEM_FORMAT = """
141
#seekto 0x0100;
142
struct {
143
   bit c[8];
144
} csetflag[32];
145

    
146
struct {
147
   u8 unknown0100[7];
148
} ropt0100;
149

    
150
#seekto 0x0120;
151
struct {
152
   bit c[8];
153
} cskipflag[32];
154

    
155
struct {
156
   u8 unknown0120[7];
157
} ropt0120;
158
"""
159

    
160
"""
161
  TH9000/UHF  memory map 
162
  section: 5  TX/RX Range
163
     used to set the TX/RX range of the radio (e.g.  144-148Mhz for 2 meter)
164
     possible to set range to 136-172Mhz for tx/rx 
165

    
166
"""
167

    
168

    
169
MEM_FORMAT = MEM_FORMAT + """
170
#seekto 0x0200;
171
struct {
172
    bbcd txrangelow[4];
173
    bbcd txrangehi[4];
174
    bbcd rxrangelow[4];
175
    bbcd rxrangehi[4];
176
} freqrange;
177
"""
178

    
179
"""
180
  TH9000/UHF  memory map 
181
  section: 6  bootup_passwd
182
     used to set bootup passwd (see boot_passwd checkbox option)
183

    
184
  options - bootup password
185

    
186
  bytes:bit   type                 description
187
  ---------------------------------------------------------------------------
188
  6         u8 bootup_passwd[6]     bootup passwd, 6 chars, numberic chars 30-39 , see boot_passwd checkbox to set
189
  10        u8 unknown;  
190

    
191
"""
192

    
193
MEM_FORMAT = MEM_FORMAT + """
194
#seekto 0x0210;
195
struct {
196
   u8 bootup_passwd[6];
197
   u8 unknown2010[10];
198
} ropt0210;
199
"""
200

    
201
"""
202
  TH9000/UHF  memory map 
203
  section: 7  Radio Options  
204
        used to set a number of radio options 
205

    
206
  bytes:bit   type                 description
207
  ---------------------------------------------------------------------------
208
  1         u8 display_mode     display mode, range 0-2, 0=freq,1=channel,2=name (selecting name affects vfo_mr)
209
  1         u8 vfo_mr;          vfo_mr , 0=vfo, mr=1 
210
  1         u8 unknown;  
211
  1         u8 squelch;         squelch level, range 0-19, hex for menu
212
  1         u8 unknown[2]; 
213
  1         u8 channel_lock;    if display_mode[channel] selected, then lock=1,no lock =0
214
  1         u8 unknown; 
215
  1         u8 bg_brightness ;  background brightness, range 0-21, hex, menu index 
216
  1         u8 unknown;     
217
  1         u8 bg_color ;       bg color, menu index,  blue 0 , orange 1, purple 2
218
  1         u8 tbst_freq ;      tbst freq , menu 0 = 1750Hz, 1=2100 , 2=1000 , 3=1450hz 
219
  1         u8 timeout_timer;   timeout timer, hex, value = minutes, 0= no timeout
220
  1         u8 unknown; 
221
  1         u8 auto_power_off;   auto power off, range 0-3, off,30min, 1hr, 2hr, hex menu index
222
  1         u8 voice_prompt;     voice prompt, value 0,1 , Beep ON = 1, Beep Off = 2
223

    
224
 description of function setup options, starting at 0x0230
225

    
226
  bytes:bit   type                 description
227
  ---------------------------------------------------------------------------
228
  1         u8  // 0
229
   :4       unknown:6
230
   :1       elim_sql_tail:1   eliminate squelsh tail when no ctcss checkbox (1=checked)
231
   :1       sql_key_function  "squelch off" 1 , "squelch momentary off" 0 , menu index
232
  2         u8 unknown[2] /1-2  
233
  1         u8 // 3
234
   :4       unknown:4
235
   :1       inhibit_init_ops:1 //bit 5
236
   :1       unknownD:1
237
   :1       inhibit_setup_bg_chk:1 //bit 7
238
   :1       unknown:1
239
  1         u8 tail_elim_type    menu , (off=0,120=1,180=2),  // 4
240
  1         u8 choose_tx_power    menu , (60w=0,25w=1) // 5
241
  2         u8 unknown[2]; // 6-7 
242
  1         u8 bootup_passwd_flag  checkbox 1=on, 0=off // 8
243
  7         u8 unknown[7]; // 9-F 
244

    
245
"""
246

    
247
MEM_FORMAT = MEM_FORMAT + """
248
#seekto 0x0220;
249
struct {
250
   u8 display_mode; 
251
   u8 vfo_mr; 
252
   u8 unknown0220A; 
253
   u8 squelch; 
254
   u8 unknown0220B[2]; 
255
   u8 channel_lock; 
256
   u8 unknown0220C; 
257
   u8 bg_brightness; 
258
   u8 unknown0220D; 
259
   u8 bg_color;
260
   u8 tbst_freq;
261
   u8 timeout_timer;
262
   u8 unknown0220E;
263
   u8 auto_power_off;
264
   u8 voice_prompt; 
265
   u8 unknown0230A:6,
266
      elim_sql_tail:1,
267
      sql_key_function:1;
268
   u8 unknown0230B[2];
269
   u8 unknown0230C:4, 
270
      inhibit_init_ops:1,
271
      unknown0230D:1,
272
      inhibit_setup_bg_chk:1,
273
      unknown0230E:1;
274
   u8 tail_elim_type;
275
   u8 choose_tx_power;
276
   u8 unknown0230F[2];
277
   u8 bootup_passwd_flag;
278
   u8 unknown0230G[7];
279
} settings;
280
"""
281

    
282
"""
283
  TH9000/UHF  memory map 
284
  section: 8B  Startup Label  
285

    
286
  bytes:bit   type                 description
287
  ---------------------------------------------------------------------------
288
  7     char start_label[7]    label displayed at startup (usually your call sign)
289
"""
290
MEM_FORMAT = MEM_FORMAT + """
291
#seekto 0x03E0;
292
struct {
293
    char startname[7];
294
} slabel;
295
"""
296

    
297
"""
298
  TH9000/UHF  memory map 
299
  section: 9  Channel Bank
300
         description of channel bank (200 channels , range 0-199)
301
         Each 32 Byte (0x20 hex)  record:
302
  bytes:bit   type                 description
303
  ---------------------------------------------------------------------------
304
  4         bbcd freq[4]        receive frequency in packed binary coded decimal  
305
  4         bbcd offset[4]      transmit offset in packed binary coded decimal (note: plus/minus direction set by 'duplex' field)
306
  1         u8
307
   :4       unknown:4
308
   :4       tuning_step:4         tuning step, menu index value from 0-9
309
            5,6.25,8.33,10,12.5,15,20,25,30,50
310
  1         u8
311
   :4       unknown:4          not yet decoded, used for DCS coding?
312
   :2       channel_width:2     channel spacing, menu index value from 0-3
313
            25,20,12.5
314
   :1       reverse:1           reverse flag, 0=off, 1=on (reverses tx and rx freqs)
315
   :1       txoff:1             transmitt off flag, 0=transmit , 1=do not transmit 
316
  1         u8
317
   :1       talkaround:1        talkaround flag, 0=off, 1=on (bypasses repeater) 
318
   :1       compander:1         compander flag, 0=off, 1=on (turns on/off voice compander option)  
319
   :2       unknown:2          
320
   :2       power:2             tx power setting, value range 0-2, 0=hi,1=med,2=lo 
321
   :2       duplex:2            duplex settings, 0=simplex,2= minus(-) offset, 3= plus (+) offset (see offset field) 
322
            
323
  1         u8 
324
   :4       unknown:4
325
   :2       rxtmode:2           rx tone mode, value range 0-2, 0=none, 1=CTCSS, 2=DCS  (ctcss tone in field rxtone)
326
   :2       txtmode:2           tx tone mode, value range 0-2, 0=none, 1=CTCSS, 3=DCS  (ctcss tone in field txtone)
327
  1         u8 
328
   :2       unknown:2
329
   :6       txtone:6            tx ctcss tone, menu index
330
  1         u8 
331
   :2       unknown:2 
332
   :6       rxtone:6            rx ctcss tone, menu index
333
  1         u8 txcode           ?, not used for ctcss
334
  1         u8 rxcode           ?, not used for ctcss
335
  3         u8 unknown[3]
336
  7         char name[7]        7 byte char string for channel name
337
  1         u8 
338
   :6       unknown:6,
339
   :2       busychannellockout:2 busy channel lockout option , 0=off, 1=repeater, 2=busy  (lock out tx if channel busy)
340
  4         u8 unknownI[4];
341
  1         u8 
342
   :7       unknown:7 
343
   :1       scrambler:1         scrambler flag, 0=off, 1=on (turns on tyt scrambler option)
344
"""
345

    
346

    
347

    
348
MEM_FORMAT = MEM_FORMAT + """
349
#seekto 0x2000;
350
struct {
351
  bbcd freq[4];
352
  bbcd offset[4];
353
  u8 unknown2000A:4,
354
     tuning_step:4;
355
  u8 unknown2000B:4,
356
     channel_width:2,
357
     reverse:1,
358
     txoff:1;
359
  u8 talkaround:1,
360
     compander:1,
361
     unknown2000C:2,
362
     power:2,
363
     duplex:2;
364
  u8 unknown2000D:4,
365
     rxtmode:2,
366
     txtmode:2;
367
  u8 unknown2000E:2,
368
     txtone:6;
369
  u8 unknown2000F:2,
370
     rxtone:6;
371
  u8 txcode;
372
  u8 rxcode;
373
  u8 unknown2000G[3];
374
  char name[7];
375
  u8 unknown2000H:6,
376
     busychannellockout:2;
377
  u8 unknown2000I[4];
378
  u8 unknown2000J:7,
379
     scrambler:1; 
380
} memory[200] ;
381
"""
382

    
383

    
384
def _debug(string):
385
    if "CHIRP_DEBUG" in os.environ or True:
386
        print string
387

    
388
def _echo_write(radio, data):
389
    try:
390
        radio.pipe.write(data)
391
        radio.pipe.read(len(data))
392
    except Exception, e:
393
        print "Error writing to radio: %s" % e
394
        raise errors.RadioError("Unable to write to radio")
395

    
396

    
397
def _checksum(data):
398
    cs = 0
399
    for byte in data:
400
        cs += ord(byte)
401
    return cs % 256
402

    
403
def _read(radio, length):
404
    try:
405
        data = radio.pipe.read(length)
406
    except Exception, e:
407
        print "Error reading from radio: %s" % e
408
        raise errors.RadioError("Unable to read from radio")
409

    
410
    if len(data) != length:
411
        print "Short read from radio (%i, expected %i)" % (len(data),
412
                                                           length)
413
        print util.hexprint(data)
414
        raise errors.RadioError("Short read from radio")
415
    return data
416

    
417

    
418

    
419
def _ident(radio):
420
    radio.pipe.setTimeout(2)
421
    _echo_write(radio,"PROGRAM")
422
    response = radio.pipe.read(3)
423
    if response != "QX\06":
424
        print "Response was :\n%s" % util.hexprint(response)
425
        raise errors.RadioError("Unsupported model - Got model: %s" % util.hexprint(response) )
426
    _echo_write(radio, "\x02")
427
    response = radio.pipe.read(16)
428
    _debug(util.hexprint(response))
429
    if response[1:8] != "TH-9000":
430
        print "Looking  for:\n%s" % util.hexprint("TH-9000")
431
        print "Response was:\n%s" % util.hexprint(response)
432
        raise errors.RadioError("Unsupported model")
433

    
434
def _send(radio, cmd, addr, length, data=None):
435
    frame = struct.pack(">cHb", cmd, addr, length)
436
    if data:
437
        frame += data
438
        frame += chr(_checksum(frame[1:]))
439
        frame += "\x06"
440
    _echo_write(radio, frame)
441
    _debug("Sent:\n%s" % util.hexprint(frame))
442
    if data:
443
        result = radio.pipe.read(1)
444
        if result != "\x06":
445
            print "Ack was: %s" % repr(result)
446
            raise errors.RadioError("Radio did not accept block at %04x" % addr)
447
        return
448
    result = _read(radio, length + 6)
449
    _debug("Got:\n%s" % util.hexprint(result))
450
    header = result[0:4]
451
    data = result[4:-2]
452
    ack = result[-1]
453
    if ack != "\x06":
454
        print "Ack was: %s" % repr(ack)
455
        raise errors.RadioError("Radio NAK'd block at %04x" % addr)
456
    _cmd, _addr, _length = struct.unpack(">cHb", header)
457
    if _addr != addr or _length != _length:
458
        print "Expected/Received:"
459
        print " Length: %02x/%02x" % (length, _length)
460
        print " Addr: %04x/%04x" % (addr, _addr)
461
        raise errors.RadioError("Radio send unexpected block")
462
    cs = _checksum(result[1:-2])
463
    if cs != ord(result[-2]):
464
        print "Calculated: %02x" % cs
465
        print "Actual:     %02x" % ord(result[-2])
466
        raise errors.RadioError("Block at 0x%04x failed checksum" % addr)
467
    return data
468

    
469

    
470
def _finish(radio):
471
    endframe = "\x45\x4E\x44"
472
    _echo_write(radio, endframe)
473
    result = radio.pipe.read(1)
474
    if result != "\x06":
475
        print "Got:\n%s" % util.hexprint(result)
476
        raise errors.RadioError("Radio did not finish cleanly")
477

    
478
def do_download(radio):
479

    
480
    _ident(radio)
481

    
482
    _memobj = None
483
    data = ""
484

    
485
    for start,end in radio._ranges: 
486
        for addr in range(start,end,0x10):
487
            block = _send(radio,'R',addr,0x10) 
488
            data += block
489
            status = chirp_common.Status()
490
            status.cur = len(data)
491
            status.max = end
492
            status.msg = "Downloading from radio"
493
            radio.status_fn(status)
494

    
495
    _finish(radio)
496

    
497
    return memmap.MemoryMap(data)
498

    
499
def do_upload(radio):
500

    
501
    _ident(radio)
502

    
503
    for start,end in radio._ranges:
504
        for addr in range(start,end,0x10):
505
            if addr < 0x0100:
506
                continue
507
            block = radio._mmap[addr:addr+0x10]
508
            _send(radio,'W',addr,len(block),block)
509
            status = chirp_common.Status()
510
            status.cur = addr
511
            status.max = end
512
            status.msg = "Uploading to Radio"
513
            radio.status_fn(status)
514

    
515
    _finish(radio)
516
            
517

    
518

    
519
@directory.register
520
class Th9000UHFRadio(chirp_common.CloneModeRadio):
521
    """TYT TH-9000 UHF"""
522
    VENDOR = "TYT"    
523
    MODEL = "TH9000" 
524
    BAUD_RATE = 9600 
525
    _file_ident = "TH-9000"
526

    
527
    _memsize = MMAPSIZE
528
    _ranges = [(0x0000,0x4000)]
529

    
530
    @classmethod
531
    def get_prompts(cls):
532
        rp = chirp_common.RadioPrompts()
533
        rp.experimental = ("The TYT TH-9000 driver is an alpha version."
534
                           "Use only for testing and development"
535
                           "Proceed with Caution and backup your data"
536
                           "as you may lose it using this driver!")
537
        return rp
538

    
539
    def get_features(self):
540
        rf = chirp_common.RadioFeatures()
541
        rf.has_settings = True
542
        rf.has_bank = False
543
        rf.has_cross = True
544
        rf.has_tuning_step = False
545
        rf.has_rx_dtcs = True
546
        rf.valid_skips = ["","S"]
547
        rf.memory_bounds = (0, 199) 
548
        rf.valid_name_length = 7
549
        rf.valid_characters = chirp_common.CHARSET_UPPER_NUMERIC + "-"
550
        rf.valid_modes = MODES
551
        rf.valid_tmodes = chirp_common.TONE_MODES
552
        rf.valid_cross_modes = CROSS_MODES
553
        rf.valid_power_levels = POWER_LEVELS
554
        rf.valid_dtcs_codes = chirp_common.ALL_DTCS_CODES
555
        rf.valid_bands = [(400000000, 490000000)]
556
        return rf
557

    
558
    # Do a download of the radio from the serial port
559
    def sync_in(self):
560
        self._mmap = do_download(self)
561
        self.process_mmap()
562

    
563
    # Do an upload of the radio to the serial port
564
    def sync_out(self):
565
        do_upload(self)
566

    
567
    def process_mmap(self):
568
        self._memobj = bitwise.parse(MEM_FORMAT, self._mmap)
569

    
570

    
571
    # Return a raw representation of the memory object, which 
572
    # is very helpful for development
573
    def get_raw_memory(self, number):
574
        return repr(self._memobj.memory[number])
575

    
576

    
577
    # not working
578
    def _get_dcs_index(self, _mem,which):
579
        base = getattr(_mem, '%scode' % which)
580
        extra = getattr(_mem, '%sdcsextra' % which)
581
        return (int(extra) << 8) | int(base)
582

    
583
    def _set_dcs_index(self, _mem, which, index):
584
        base = getattr(_mem, '%scode' % which)
585
        extra = getattr(_mem, '%sdcsextra' % which)
586
        base.set_value(index & 0xFF)
587
        extra.set_value(index >> 8)
588

    
589

    
590
    # Extract a high-level memory object from the low-level memory map
591
    # This is called to populate a memory in the UI
592
    def get_memory(self, number):
593
        # Get a low-level memory object mapped to the image
594
        _mem = self._memobj.memory[number]
595

    
596
        # get flag info
597
        cbyte = number / 8 ;
598
        cbit =  7 - (number % 8) ;
599
        setflag = self._memobj.csetflag[cbyte].c[cbit]; 
600
        skipflag = self._memobj.cskipflag[cbyte].c[cbit]; 
601

    
602
        mem = chirp_common.Memory()
603

    
604
        mem.number = number  # Set the memory number
605

    
606
        if setflag == 1:
607
            mem.empty = True
608
            return mem
609

    
610
        mem.freq = int(_mem.freq) * 100    
611
        mem.offset = int(_mem.offset) * 100
612
        mem.name = str(_mem.name).rstrip() # Set the alpha tag
613
        mem.duplex = DUPLEXES[_mem.duplex]
614
        mem.mode = MODES[_mem.channel_width]
615
        mem.power = POWER_LEVELS[_mem.power]
616

    
617
        rxtone = txtone = None
618

    
619

    
620
        rxmode = TMODES[_mem.rxtmode]
621
        txmode = TMODES[_mem.txtmode]
622

    
623

    
624
        rxpol = txpol =  ""
625

    
626
        # doesn't work
627
        if rxmode == "Tone":
628
            rxpol = ""
629
            rxtone = TONES[_mem.rxtone]
630
        elif rxmode == "DTCS":
631
            rxpol = "N"
632
            rxtone = chirp_common.ALL_DTCS_CODES[self._get_dcs_index(_mem,'rx')]
633

    
634
        if txmode == "Tone":
635
            txpol = ""
636
            txtone = TONES[_mem.txtone]
637
        elif txmode == "DTCS":
638
            txpol = "N"
639
            txtone = chirp_common.ALL_DTCS_CODES[self._get_dcs_index(_mem,'tx')]
640

    
641

    
642
        chirp_common.split_tone_decode(mem,
643
                                       (txmode, txtone, txpol),
644
                                       (rxmode, rxtone, rxpol))
645

    
646
        mem.skip = "S" if skipflag == 1 else ""
647

    
648

    
649
        # We'll consider any blank (i.e. 0MHz frequency) to be empty
650
        if mem.freq == 0:
651
            mem.empty = True
652

    
653
        return mem
654

    
655
    # Store details about a high-level memory to the memory map
656
    # This is called when a user edits a memory in the UI
657
    def set_memory(self, mem):
658
        # Get a low-level memory object mapped to the image
659

    
660
        _mem = self._memobj.memory[mem.number]
661

    
662
        cbyte = mem.number / 8 
663
        cbit =  7 - (mem.number % 8) 
664

    
665
        if mem.empty:
666
            self._memobj.csetflag[cbyte].c[cbit] = 1
667
            self._memobj.cskipflag[cbyte].c[cbit] = 1
668
            return
669

    
670
        self._memobj.csetflag[cbyte].c[cbit] =  0 
671
        self._memobj.cskipflag[cbyte].c[cbit]  =  1 if (mem.skip == "S") else 0
672

    
673
        _mem.set_raw("\x00" * 32)
674

    
675
        _mem.freq = mem.freq / 100         # Convert to low-level frequency
676
        _mem.offset = mem.offset / 100         # Convert to low-level frequency
677

    
678
        _mem.name = mem.name.ljust(7)[:7]  # Store the alpha tag
679
        _mem.duplex = DUPLEXES.index(mem.duplex)
680

    
681

    
682
        try:
683
            _mem.channel_width = MODES.index(mem.mode)
684
        except ValueError:
685
            _mem.channel_width = 0
686

    
687
        ((txmode, txtone, txpol),
688
         (rxmode, rxtone, rxpol)) = chirp_common.split_tone_encode(mem)
689

    
690
        _mem.txtmode = TMODES.index(txmode)
691
        _mem.rxtmode = TMODES.index(rxmode)
692

    
693
        if txmode == "Tone":
694
            _mem.txtone = TONES.index(txtone)
695
        elif txmode == "DTCS":
696
            self._set_dcs_index(_mem,'tx',chirp_common.ALL_DTCS_CODES.index(txtone))
697

    
698
        if rxmode == "Tone":
699
            _mem.rxtone = TONES.index(rxtone)
700
        elif rxmode == "DTCS":
701
            self._set_dcs_index(_mem, 'rx', chirp_common.ALL_DTCS_CODES.index(rxtone))
702

    
703
        #_mem.txinv = txpol == "N"
704
        #_mem.rxinv = rxpol == "N"
705

    
706
       
707
        if mem.power:
708
            _mem.power = POWER_LEVELS.index(mem.power)
709
        else:
710
            _mem.power = 0
711

    
712
    def _get_settings(self):
713
        _settings = self._memobj.settings
714
        _freqrange = self._memobj.freqrange
715
        _slabel = self._memobj.slabel
716

    
717
        basic = RadioSettingGroup("basic","Global Settings")
718
        freqrange = RadioSettingGroup("freqrange","Frequency Ranges")
719
        top = RadioSettingGroup("top","All Settings",basic,freqrange)
720

    
721
        def _filter(name):
722
            filtered = ""
723
            for char in str(name):
724
                if char in chirp_common.CHARSET_ASCII:
725
                    filtered += char
726
                else:
727
                    filtered += ""
728
            return filtered
729
                   
730
        val = RadioSettingValueString(0,7,_filter(_slabel.startname))
731
        rs = RadioSetting("startname","Startup Label",val)
732
        basic.append(rs)
733

    
734
        rs = RadioSetting("bg_color","LCD Color",
735
                           RadioSettingValueList(BGCOLOR_LIST, BGCOLOR_LIST[_settings.bg_color]))
736
        basic.append(rs)
737

    
738
        rs = RadioSetting("bg_brightness","LCD Brightness",
739
                           RadioSettingValueList(BGBRIGHT_LIST, BGBRIGHT_LIST[_settings.bg_brightness]))
740
        basic.append(rs)
741

    
742
        rs = RadioSetting("squelch","Squelch Level",
743
                           RadioSettingValueList(SQUELCH_LIST, SQUELCH_LIST[_settings.squelch]))
744
        basic.append(rs)
745

    
746
        rs = RadioSetting("timeout_timer","Timeout Timer (TOT)",
747
                           RadioSettingValueList(TIMEOUT_LIST, TIMEOUT_LIST[_settings.timeout_timer]))
748
        basic.append(rs)
749

    
750
        rs = RadioSetting("auto_power_off","Auto Power Off (APO)",
751
                           RadioSettingValueList(APO_LIST, APO_LIST[_settings.auto_power_off]))
752
        basic.append(rs)
753

    
754
        rs = RadioSetting("voice_prompt","Beep Prompt",
755
                           RadioSettingValueList(BEEP_LIST, BEEP_LIST[_settings.voice_prompt]))
756
        basic.append(rs)
757

    
758
        rs = RadioSetting("tbst_freq","Tone Burst Frequency",
759
                           RadioSettingValueList(TBSTFREQ_LIST, TBSTFREQ_LIST[_settings.tbst_freq]))
760
        basic.append(rs)
761

    
762
        rs = RadioSetting("choose_tx_power","Max Level of TX Power",
763
                           RadioSettingValueList(TXPWR_LIST, TXPWR_LIST[_settings.choose_tx_power]))
764
        basic.append(rs)
765

    
766
        rs = RadioSetting("txrangelow","TX Freq, Lower Limit (khz)", RadioSettingValueInteger(400000,400000, int(_freqrange.txrangelow)/10))
767
        freqrange.append(rs)
768

    
769
        rs = RadioSetting("txrangehi","TX Freq, Upper Limit (khz)", RadioSettingValueInteger(490000,490000,int(_freqrange.txrangehi)/10))
770
        freqrange.append(rs)
771

    
772
        rs = RadioSetting("rxrangelow","RX Freq, Lower Limit (khz)", RadioSettingValueInteger(400000,400000, int(_freqrange.rxrangelow)/10))
773
        freqrange.append(rs)
774

    
775
        rs = RadioSetting("rxrangehi","RX Freq, Upper Limit (khz)", RadioSettingValueInteger(490000,490000,int(_freqrange.rxrangehi)/10))
776
        freqrange.append(rs)
777

    
778
        return top
779

    
780
    def get_settings(self):
781
        try:
782
            return self._get_settings()
783
        except:
784
            import traceback
785
            print "failed to parse settings"
786
            traceback.print_exc()
787
            return None
788

    
789
    def set_settings(self,settings):
790
        _settings = self._memobj.settings
791
        for element in settings:
792
            if not isinstance(element,RadioSetting):
793
                self.set_settings(element)
794
                continue
795
            else:
796
                try:
797
                    name = element.get_name()
798

    
799
                    if  name in ["txrangelow","txrangehi","rxrangelow","rxrangehi"]:
800
                        print "setting %s = %s" % (name,int(element.value)*10)
801
                        setattr(self._memobj.freqrange,name,int(element.value)*10)
802
                        continue
803

    
804
                    if name in ["startname"]:
805
                        print "setting %s = %s" % (name, element.value)
806
                        setattr(self._memobj.slabel,name,element.value)
807
                        continue
808

    
809
                    obj = _settings
810
                    setting = element.get_name()
811

    
812
                    if element.has_apply_callback():
813
                        print "using apply callback"
814
                        element.run_apply_callback()
815
                    else:
816
                        print "Setting %s = %s" % (setting, element.value)
817
                        setattr(obj, setting, element.value)
818
                except Exception, e:
819
                    print element.get_name()
820
                    raise
821

    
822
    @classmethod
823
    def match_model(cls, filedata, filename):
824
        return cls._file_ident in filedata[0x10:0x20]
(7-7/14)