Project

General

Profile

New Model #1035 » th9000.py

source file - David Fannin, 04/18/2015 10:16 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
import logging
20

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

    
32
#from chirp.settings import RadioSetting, RadioSettingGroup, \
33
#        RadioSettingValueInteger, RadioSettingValueList, \
34
#        RadioSettingValueBoolean, RadioSettingValueString, \
35
#        RadioSettingValueFloat, InvalidValueError
36

    
37
LOG = logging.getLogger(__name__)
38

    
39
#
40
#  Chirp Driver for TYT TH-9000D  Radio (2 meter, 1.25 and 70cm radios)
41
#  by David Fannin <dfannin@sushisoft.com>, KK6DF
42
#
43
#  Version 0.5 (Experimental - Known Bugs and Issues)
44
#  Use for development purposes only!  
45
#  Features working:
46
#         - single class file for 3 radio types - 144, 220 and 440 mhz models)
47
#         - Download from Radio
48
#         - Display Memories (only None, Tone, TSQL signalling supported)
49
#         - Save image file
50
#         - memory map decoded (about 90%)
51
#         - Upload to radio
52
#         - Modification of memories
53
#         - feature settings
54
#         - added Startup ID label
55
#
56
#  Features not working:
57
#         - DCS , Cross Signaling
58
#         - Skip channels
59

    
60

    
61
#
62
# Global Parameters 
63
#
64
MMAPSIZE = 16128
65
TONES = [62.5] + list(chirp_common.TONES)
66
TMODES =  ['','Tone','DTCS'] 
67
DUPLEXES = ['','err','-','+'] # index 2 not used
68
MODES = ['WFM','FM','NFM']  #  25k, 20k,15k bw 
69
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
70
POWER_LEVELS=[chirp_common.PowerLevel("High", watts=65),
71
              chirp_common.PowerLevel("Mid", watts=25),
72
              chirp_common.PowerLevel("Low", watts=10)]
73

    
74
CROSS_MODES = chirp_common.CROSS_MODES
75

    
76
APO_LIST = [ "Off","30 min","1 hr","2 hrs" ] 
77
BGCOLOR_LIST = ["Blue","Orange","Purple"]
78
BGBRIGHT_LIST = ["%s" % x for x in range(1,32)]
79
SQUELCH_LIST = ["Off"] + ["Level %s" % x for x in range(1,20)] 
80
TIMEOUT_LIST = ["Off"] + ["%s min" % x for x in range(1,30)]
81
TXPWR_LIST = ["60W","25W"]  # maximum power for Hi setting
82
TBSTFREQ_LIST = ["1750Hz","2100Hz","1000Hz","1450Hz"]
83
BEEP_LIST = ["Off","On"]
84

    
85
SETTING_LISTS = {
86
        "auto_power_off": APO_LIST,
87
        "bg_color"      : BGCOLOR_LIST,
88
        "bg_brightness" : BGBRIGHT_LIST,
89
        "squelch"       : SQUELCH_LIST,
90
        "timeout_timer" : TIMEOUT_LIST,
91
        "choose_tx_power": TXPWR_LIST,
92
        "tbst_freq"     : TBSTFREQ_LIST,
93
        "voice_prompt"  : BEEP_LIST
94
}
95

    
96

    
97
#
98
#
99
#
100

    
101
MEM_FORMAT = """
102
#seekto 0x0000;
103
struct {
104
   u8 unknown0000[16];
105
   char idhdr[16];
106
   u8 unknown0001[16];
107
} fidhdr;
108
"""
109

    
110
"""
111
Overall Memory Map:
112

    
113
    Memory Map (Range 0x0100-3FF0, step 0x10):
114

    
115
        Field                   Start  End  Size   
116
                                (hex)  (hex) (hex)  
117
        
118
        1 Channel Set Flag        0100  011F   20 
119
        2 Channel Skip Flag       0120  013F   20 
120
        3 Blank/Unknown           0140  01EF   B0 
121
        4 Unknown                 01F0  01FF   10
122
        5 TX/RX Range             0200  020F   10    
123
        6 Bootup Passwd           0210  021F   10 
124
        7 Options, Radio          0220  023F   20
125
        8 Unknown                 0240  019F   
126
            8B Startup Label      03E0  03E7   07
127
        9 Channel Bank            2000  38FF 1900  
128
             Channel 000          2000  201F   20  
129
             Channel 001          2020  202F   20  
130
             ... 
131
             Channel 199          38E0  38FF   20 
132
        10 Blank/Unknown          3900  3FFF  6FF  14592  16383    1792   
133
            Total Map Size           16128 (2^8 = 16384)
134

    
135

    
136
"""
137

    
138
"""
139
  TH9000/220  memory map 
140
  section: 1 and 2:  Channel Set/Skip Flags
141

    
142
    Channel Set (starts 0x100) : Channel Set  bit is value 0 if a memory location in the channel bank is active.
143
    Channel Skip (starts 0x120): Channel Skip bit is value 0 if a memory location in the channel bank is active.
144

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

    
148
    to index a channel:
149
        cbyte = channel / 8 ;
150
        cbit  = channel % 8 ;
151
        setflag  = csetflag[cbyte].c[cbit] ;
152
        skipflag = cskipflag[cbyte].c[cbit] ;
153

    
154
    channel range is 0-199, range is 32 bytes (last 7 unknown)
155
"""
156

    
157

    
158
MEM_FORMAT = MEM_FORMAT + """
159
#seekto 0x0100;
160
struct {
161
   bit c[8];
162
} csetflag[32];
163

    
164
struct {
165
   u8 unknown0100[7];
166
} ropt0100;
167

    
168
#seekto 0x0120;
169
struct {
170
   bit c[8];
171
} cskipflag[32];
172

    
173
struct {
174
   u8 unknown0120[7];
175
} ropt0120;
176
"""
177

    
178
"""
179
  TH9000/220  memory map 
180
  section: 5  TX/RX Range
181
     used to set the TX/RX range of the radio (e.g.  222-228Mhz for 220 meter)
182
     possible to set range to 220-260Mhz for tx/rx 
183

    
184
"""
185

    
186

    
187
MEM_FORMAT = MEM_FORMAT + """
188
#seekto 0x0200;
189
struct {
190
    bbcd txrangelow[4];
191
    bbcd txrangehi[4];
192
    bbcd rxrangelow[4];
193
    bbcd rxrangehi[4];
194
} freqrange;
195
"""
196

    
197
"""
198
  TH9000/220  memory map 
199
  section: 6  bootup_passwd
200
     used to set bootup passwd (see boot_passwd checkbox option)
201

    
202
  options - bootup password
203

    
204
  bytes:bit   type                 description
205
  ---------------------------------------------------------------------------
206
  6         u8 bootup_passwd[6]     bootup passwd, 6 chars, numberic chars 30-39 , see boot_passwd checkbox to set
207
  10        u8 unknown;  
208

    
209
"""
210

    
211
MEM_FORMAT = MEM_FORMAT + """
212
#seekto 0x0210;
213
struct {
214
   u8 bootup_passwd[6];
215
   u8 unknown2010[10];
216
} ropt0210;
217
"""
218

    
219
"""
220
  TH9000/220  memory map 
221
  section: 7  Radio Options  
222
        used to set a number of radio options 
223

    
224
  bytes:bit   type                 description
225
  ---------------------------------------------------------------------------
226
  1         u8 display_mode     display mode, range 0-2, 0=freq,1=channel,2=name (selecting name affects vfo_mr)
227
  1         u8 vfo_mr;          vfo_mr , 0=vfo, mr=1 
228
  1         u8 unknown;  
229
  1         u8 squelch;         squelch level, range 0-19, hex for menu
230
  1         u8 unknown[2]; 
231
  1         u8 channel_lock;    if display_mode[channel] selected, then lock=1,no lock =0
232
  1         u8 unknown; 
233
  1         u8 bg_brightness ;  background brightness, range 0-21, hex, menu index 
234
  1         u8 unknown;     
235
  1         u8 bg_color ;       bg color, menu index,  blue 0 , orange 1, purple 2
236
  1         u8 tbst_freq ;      tbst freq , menu 0 = 1750Hz, 1=2100 , 2=1000 , 3=1450hz 
237
  1         u8 timeout_timer;   timeout timer, hex, value = minutes, 0= no timeout
238
  1         u8 unknown; 
239
  1         u8 auto_power_off;   auto power off, range 0-3, off,30min, 1hr, 2hr, hex menu index
240
  1         u8 voice_prompt;     voice prompt, value 0,1 , Beep ON = 1, Beep Off = 2
241

    
242
 description of function setup options, starting at 0x0230
243

    
244
  bytes:bit   type                 description
245
  ---------------------------------------------------------------------------
246
  1         u8  // 0
247
   :4       unknown:6
248
   :1       elim_sql_tail:1   eliminate squelsh tail when no ctcss checkbox (1=checked)
249
   :1       sql_key_function  "squelch off" 1 , "squelch momentary off" 0 , menu index
250
  2         u8 unknown[2] /1-2  
251
  1         u8 // 3
252
   :4       unknown:4
253
   :1       inhibit_init_ops:1 //bit 5
254
   :1       unknownD:1
255
   :1       inhibit_setup_bg_chk:1 //bit 7
256
   :1       unknown:1
257
  1         u8 tail_elim_type    menu , (off=0,120=1,180=2),  // 4
258
  1         u8 choose_tx_power    menu , (60w=0,25w=1) // 5
259
  2         u8 unknown[2]; // 6-7 
260
  1         u8 bootup_passwd_flag  checkbox 1=on, 0=off // 8
261
  7         u8 unknown[7]; // 9-F 
262

    
263
"""
264

    
265
MEM_FORMAT = MEM_FORMAT + """
266
#seekto 0x0220;
267
struct {
268
   u8 display_mode; 
269
   u8 vfo_mr; 
270
   u8 unknown0220A; 
271
   u8 squelch; 
272
   u8 unknown0220B[2]; 
273
   u8 channel_lock; 
274
   u8 unknown0220C; 
275
   u8 bg_brightness; 
276
   u8 unknown0220D; 
277
   u8 bg_color;
278
   u8 tbst_freq;
279
   u8 timeout_timer;
280
   u8 unknown0220E;
281
   u8 auto_power_off;
282
   u8 voice_prompt; 
283
   u8 unknown0230A:6,
284
      elim_sql_tail:1,
285
      sql_key_function:1;
286
   u8 unknown0230B[2];
287
   u8 unknown0230C:4, 
288
      inhibit_init_ops:1,
289
      unknown0230D:1,
290
      inhibit_setup_bg_chk:1,
291
      unknown0230E:1;
292
   u8 tail_elim_type;
293
   u8 choose_tx_power;
294
   u8 unknown0230F[2];
295
   u8 bootup_passwd_flag;
296
   u8 unknown0230G[7];
297
} settings;
298
"""
299

    
300
"""
301
  TH9000/220  memory map 
302
  section: 8B  Startup Label  
303

    
304
  bytes:bit   type                 description
305
  ---------------------------------------------------------------------------
306
  7     char start_label[7]    label displayed at startup (usually your call sign)
307
"""
308
MEM_FORMAT = MEM_FORMAT + """
309
#seekto 0x03E0;
310
struct {
311
    char startname[7];
312
} slabel;
313
"""
314

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

    
364

    
365

    
366
MEM_FORMAT = MEM_FORMAT + """
367
#seekto 0x2000;
368
struct {
369
  bbcd freq[4];
370
  bbcd offset[4];
371
  u8 unknown2000A:4,
372
     tuning_step:4;
373
  u8 unknown2000B:4,
374
     channel_width:2,
375
     reverse:1,
376
     txoff:1;
377
  u8 talkaround:1,
378
     compander:1,
379
     unknown2000C:2,
380
     power:2,
381
     duplex:2;
382
  u8 unknown2000D:4,
383
     rxtmode:2,
384
     txtmode:2;
385
  u8 unknown2000E:2,
386
     txtone:6;
387
  u8 unknown2000F:2,
388
     rxtone:6;
389
  u8 txcode;
390
  u8 rxcode;
391
  u8 unknown2000G[3];
392
  char name[7];
393
  u8 unknown2000H:6,
394
     busychannellockout:2;
395
  u8 unknown2000I[4];
396
  u8 unknown2000J:7,
397
     scrambler:1; 
398
} memory[200] ;
399
"""
400

    
401

    
402
def _echo_write(radio, data):
403
    try:
404
        radio.pipe.write(data)
405
        radio.pipe.read(len(data))
406
    except Exception, e:
407
        LOG.error("Error writing to radio: %s" % e)
408
        raise errors.RadioError("Unable to write to radio")
409

    
410

    
411
def _checksum(data):
412
    cs = 0
413
    for byte in data:
414
        cs += ord(byte)
415
    return cs % 256
416

    
417
def _read(radio, length):
418
    try:
419
        data = radio.pipe.read(length)
420
    except Exception, e:
421
        LOG.error( "Error reading from radio: %s" % e)
422
        raise errors.RadioError("Unable to read from radio")
423

    
424
    if len(data) != length:
425
        LOG.error( "Short read from radio (%i, expected %i)" % (len(data),
426
                                                           length))
427
        LOG.debug(util.hexprint(data))
428
        raise errors.RadioError("Short read from radio")
429
    return data
430

    
431

    
432

    
433
def _ident(radio):
434
    radio.pipe.setTimeout(1)
435
    _echo_write(radio,"PROGRAM")
436
    response = radio.pipe.read(3)
437
    if response != "QX\06":
438
        LOG.debug( "Response was :\n%s" % util.hexprint(response))
439
        raise errors.RadioError("Unsupported model")
440
    _echo_write(radio, "\x02")
441
    response = radio.pipe.read(16)
442
    LOG.debug(util.hexprint(response))
443
    if response[1:8] != "TH-9000":
444
        LOG.error( "Looking  for:\n%s" % util.hexprint("TH-9000"))
445
        LOG.error( "Response was:\n%s" % util.hexprint(response))
446
        raise errors.RadioError("Unsupported model")
447

    
448
def _send(radio, cmd, addr, length, data=None):
449
    frame = struct.pack(">cHb", cmd, addr, length)
450
    if data:
451
        frame += data
452
        frame += chr(_checksum(frame[1:]))
453
        frame += "\x06"
454
    _echo_write(radio, frame)
455
    LOG.debug("Sent:\n%s" % util.hexprint(frame))
456
    if data:
457
        result = radio.pipe.read(1)
458
        if result != "\x06":
459
            LOG.debug( "Ack was: %s" % repr(result))
460
            raise errors.RadioError("Radio did not accept block at %04x" % addr)
461
        return
462
    result = _read(radio, length + 6)
463
    LOG.debug("Got:\n%s" % util.hexprint(result))
464
    header = result[0:4]
465
    data = result[4:-2]
466
    ack = result[-1]
467
    if ack != "\x06":
468
        LOG.debug("Ack was: %s" % repr(ack))
469
        raise errors.RadioError("Radio NAK'd block at %04x" % addr)
470
    _cmd, _addr, _length = struct.unpack(">cHb", header)
471
    if _addr != addr or _length != _length:
472
        LOG.debug( "Expected/Received:")
473
        LOG.debug(" Length: %02x/%02x" % (length, _length))
474
        LOG.debug( " Addr: %04x/%04x" % (addr, _addr))
475
        raise errors.RadioError("Radio send unexpected block")
476
    cs = _checksum(result[1:-2])
477
    if cs != ord(result[-2]):
478
        LOG.debug( "Calculated: %02x" % cs)
479
        LOG.debug( "Actual:     %02x" % ord(result[-2]))
480
        raise errors.RadioError("Block at 0x%04x failed checksum" % addr)
481
    return data
482

    
483

    
484
def _finish(radio):
485
    endframe = "\x45\x4E\x44"
486
    _echo_write(radio, endframe)
487
    result = radio.pipe.read(1)
488
    if result != "\x06":
489
        LOG.error( "Got:\n%s" % util.hexprint(result))
490
        raise errors.RadioError("Radio did not finish cleanly")
491

    
492
def do_download(radio):
493

    
494
    _ident(radio)
495

    
496
    _memobj = None
497
    data = ""
498

    
499
    for start,end in radio._ranges: 
500
        for addr in range(start,end,0x10):
501
            block = _send(radio,'R',addr,0x10) 
502
            data += block
503
            status = chirp_common.Status()
504
            status.cur = len(data)
505
            status.max = end
506
            status.msg = "Downloading from radio"
507
            radio.status_fn(status)
508

    
509
    _finish(radio)
510

    
511
    return memmap.MemoryMap(data)
512

    
513
def do_upload(radio):
514

    
515
    _ident(radio)
516

    
517
    for start,end in radio._ranges:
518
        for addr in range(start,end,0x10):
519
            if addr < 0x0100:
520
                continue
521
            block = radio._mmap[addr:addr+0x10]
522
            _send(radio,'W',addr,len(block),block)
523
            status = chirp_common.Status()
524
            status.cur = addr
525
            status.max = end
526
            status.msg = "Uploading to Radio"
527
            radio.status_fn(status)
528

    
529
    _finish(radio)
530
            
531

    
532

    
533
@directory.register
534
class Th9000Radio(chirp_common.CloneModeRadio,
535
                  chirp_common.ExperimentalRadio):
536
    """TYT TH-9000"""
537
    VENDOR = "TYT"    
538
    MODEL = "TH9000" 
539
    BAUD_RATE = 9600 
540
    _file_ident = "TH9000 XXX"
541
    valid_freq = [(220000000, 260000000)]
542
    
543

    
544
    _memsize = MMAPSIZE
545
    _ranges = [(0x0000,0x4000)]
546

    
547
    @classmethod
548
    def get_prompts(cls):
549
        rp = chirp_common.RadioPrompts()
550
        rp.experimental = ("The TYT TH-9000 driver is an alpha version."
551
                           "Use only for testing and development"
552
                           "Proceed with Caution and backup your data"
553
                           "as you may lose it using this driver!")
554
        return rp
555

    
556
    def get_features(self):
557
        rf = chirp_common.RadioFeatures()
558
        rf.has_settings = True
559
        rf.has_bank = False
560
        rf.has_cross = True
561
        rf.has_tuning_step = False
562
        rf.has_rx_dtcs = True
563
        rf.valid_skips = ["","S"]
564
        rf.memory_bounds = (0, 199) 
565
        rf.valid_name_length = 7
566
        rf.valid_characters = chirp_common.CHARSET_UPPER_NUMERIC + "-"
567
        rf.valid_modes = MODES
568
        rf.valid_tmodes = chirp_common.TONE_MODES
569
        rf.valid_cross_modes = CROSS_MODES
570
        rf.valid_power_levels = POWER_LEVELS
571
        rf.valid_dtcs_codes = chirp_common.ALL_DTCS_CODES
572
        rf.valid_bands = self.valid_freq
573
        return rf
574

    
575
    # Do a download of the radio from the serial port
576
    def sync_in(self):
577
        self._mmap = do_download(self)
578
        self.process_mmap()
579

    
580
    # Do an upload of the radio to the serial port
581
    def sync_out(self):
582
        do_upload(self)
583

    
584
    def process_mmap(self):
585
        self._memobj = bitwise.parse(MEM_FORMAT, self._mmap)
586

    
587

    
588
    # Return a raw representation of the memory object, which 
589
    # is very helpful for development
590
    def get_raw_memory(self, number):
591
        return repr(self._memobj.memory[number])
592

    
593

    
594
    # not working
595
    def _get_dcs_index(self, _mem,which):
596
        base = getattr(_mem, '%scode' % which)
597
        extra = getattr(_mem, '%sdcsextra' % which)
598
        return (int(extra) << 8) | int(base)
599

    
600
    def _set_dcs_index(self, _mem, which, index):
601
        base = getattr(_mem, '%scode' % which)
602
        extra = getattr(_mem, '%sdcsextra' % which)
603
        base.set_value(index & 0xFF)
604
        extra.set_value(index >> 8)
605

    
606

    
607
    # Extract a high-level memory object from the low-level memory map
608
    # This is called to populate a memory in the UI
609
    def get_memory(self, number):
610
        # Get a low-level memory object mapped to the image
611
        _mem = self._memobj.memory[number]
612

    
613
        # get flag info
614
        cbyte = number / 8 ;
615
        cbit =  7 - (number % 8) ;
616
        setflag = self._memobj.csetflag[cbyte].c[cbit]; 
617
        skipflag = self._memobj.cskipflag[cbyte].c[cbit]; 
618

    
619
        mem = chirp_common.Memory()
620

    
621
        mem.number = number  # Set the memory number
622

    
623
        if setflag == 1:
624
            mem.empty = True
625
            return mem
626

    
627
        mem.freq = int(_mem.freq) * 100    
628
        mem.offset = int(_mem.offset) * 100
629
        mem.name = str(_mem.name).rstrip() # Set the alpha tag
630
        mem.duplex = DUPLEXES[_mem.duplex]
631
        mem.mode = MODES[_mem.channel_width]
632
        mem.power = POWER_LEVELS[_mem.power]
633

    
634
        rxtone = txtone = None
635

    
636

    
637
        rxmode = TMODES[_mem.rxtmode]
638
        txmode = TMODES[_mem.txtmode]
639

    
640

    
641
        rxpol = txpol =  ""
642

    
643
        # doesn't work
644
        if rxmode == "Tone":
645
            rxpol = ""
646
            rxtone = TONES[_mem.rxtone]
647
        elif rxmode == "DTCS":
648
            rxpol = "N"
649
            rxtone = chirp_common.ALL_DTCS_CODES[self._get_dcs_index(_mem,'rx')]
650

    
651
        if txmode == "Tone":
652
            txpol = ""
653
            txtone = TONES[_mem.txtone]
654
        elif txmode == "DTCS":
655
            txpol = "N"
656
            txtone = chirp_common.ALL_DTCS_CODES[self._get_dcs_index(_mem,'tx')]
657

    
658

    
659
        chirp_common.split_tone_decode(mem,
660
                                       (txmode, txtone, txpol),
661
                                       (rxmode, rxtone, rxpol))
662

    
663
        mem.skip = "S" if skipflag == 1 else ""
664

    
665

    
666
        # We'll consider any blank (i.e. 0MHz frequency) to be empty
667
        if mem.freq == 0:
668
            mem.empty = True
669

    
670
        return mem
671

    
672
    # Store details about a high-level memory to the memory map
673
    # This is called when a user edits a memory in the UI
674
    def set_memory(self, mem):
675
        # Get a low-level memory object mapped to the image
676

    
677
        _mem = self._memobj.memory[mem.number]
678

    
679
        cbyte = mem.number / 8 
680
        cbit =  7 - (mem.number % 8) 
681

    
682
        if mem.empty:
683
            self._memobj.csetflag[cbyte].c[cbit] = 1
684
            self._memobj.cskipflag[cbyte].c[cbit] = 1
685
            return
686

    
687
        self._memobj.csetflag[cbyte].c[cbit] =  0 
688
        self._memobj.cskipflag[cbyte].c[cbit]  =  1 if (mem.skip == "S") else 0
689

    
690
        _mem.set_raw("\x00" * 32)
691

    
692
        _mem.freq = mem.freq / 100         # Convert to low-level frequency
693
        _mem.offset = mem.offset / 100         # Convert to low-level frequency
694

    
695
        _mem.name = mem.name.ljust(7)[:7]  # Store the alpha tag
696
        _mem.duplex = DUPLEXES.index(mem.duplex)
697

    
698

    
699
        try:
700
            _mem.channel_width = MODES.index(mem.mode)
701
        except ValueError:
702
            _mem.channel_width = 0
703

    
704
        ((txmode, txtone, txpol),
705
         (rxmode, rxtone, rxpol)) = chirp_common.split_tone_encode(mem)
706

    
707
        _mem.txtmode = TMODES.index(txmode)
708
        _mem.rxtmode = TMODES.index(rxmode)
709

    
710
        if txmode == "Tone":
711
            _mem.txtone = TONES.index(txtone)
712
        elif txmode == "DTCS":
713
            self._set_dcs_index(_mem,'tx',chirp_common.ALL_DTCS_CODES.index(txtone))
714

    
715
        if rxmode == "Tone":
716
            _mem.rxtone = TONES.index(rxtone)
717
        elif rxmode == "DTCS":
718
            self._set_dcs_index(_mem, 'rx', chirp_common.ALL_DTCS_CODES.index(rxtone))
719

    
720
        #_mem.txinv = txpol == "N"
721
        #_mem.rxinv = rxpol == "N"
722

    
723
       
724
        if mem.power:
725
            _mem.power = POWER_LEVELS.index(mem.power)
726
        else:
727
            _mem.power = 0
728

    
729
    def _get_settings(self):
730
        _settings = self._memobj.settings
731
        _freqrange = self._memobj.freqrange
732
        _slabel = self._memobj.slabel
733

    
734
        basic = RadioSettingGroup("basic","Global Settings")
735
        freqrange = RadioSettingGroup("freqrange","Frequency Ranges")
736
        top = RadioSettingGroup("top","All Settings",basic,freqrange)
737
        settings = RadioSettings(top)
738

    
739
        def _filter(name):
740
            filtered = ""
741
            for char in str(name):
742
                if char in chirp_common.CHARSET_ASCII:
743
                    filtered += char
744
                else:
745
                    filtered += ""
746
            return filtered
747
                   
748
        val = RadioSettingValueString(0,7,_filter(_slabel.startname))
749
        rs = RadioSetting("startname","Startup Label",val)
750
        basic.append(rs)
751

    
752
        rs = RadioSetting("bg_color","LCD Color",
753
                           RadioSettingValueList(BGCOLOR_LIST, BGCOLOR_LIST[_settings.bg_color]))
754
        basic.append(rs)
755

    
756
        rs = RadioSetting("bg_brightness","LCD Brightness",
757
                           RadioSettingValueList(BGBRIGHT_LIST, BGBRIGHT_LIST[_settings.bg_brightness]))
758
        basic.append(rs)
759

    
760
        rs = RadioSetting("squelch","Squelch Level",
761
                           RadioSettingValueList(SQUELCH_LIST, SQUELCH_LIST[_settings.squelch]))
762
        basic.append(rs)
763

    
764
        rs = RadioSetting("timeout_timer","Timeout Timer (TOT)",
765
                           RadioSettingValueList(TIMEOUT_LIST, TIMEOUT_LIST[_settings.timeout_timer]))
766
        basic.append(rs)
767

    
768
        rs = RadioSetting("auto_power_off","Auto Power Off (APO)",
769
                           RadioSettingValueList(APO_LIST, APO_LIST[_settings.auto_power_off]))
770
        basic.append(rs)
771

    
772
        rs = RadioSetting("voice_prompt","Beep Prompt",
773
                           RadioSettingValueList(BEEP_LIST, BEEP_LIST[_settings.voice_prompt]))
774
        basic.append(rs)
775

    
776
        rs = RadioSetting("tbst_freq","Tone Burst Frequency",
777
                           RadioSettingValueList(TBSTFREQ_LIST, TBSTFREQ_LIST[_settings.tbst_freq]))
778
        basic.append(rs)
779

    
780
        rs = RadioSetting("choose_tx_power","Max Level of TX Power",
781
                           RadioSettingValueList(TXPWR_LIST, TXPWR_LIST[_settings.choose_tx_power]))
782
        basic.append(rs)
783

    
784
        (flow,fhigh)  = self.valid_freq[0]
785
        flow  /= 1000
786
        fhigh /= 1000
787
        fmidrange = (fhigh- flow)/2000
788

    
789
        rs = RadioSetting("txrangelow","TX Freq, Lower Limit (khz)", RadioSettingValueInteger(flow,
790
            flow + fmidrange,
791
            int(_freqrange.txrangelow)/10))
792
        freqrange.append(rs)
793

    
794
        rs = RadioSetting("txrangehi","TX Freq, Upper Limit (khz)", RadioSettingValueInteger(fhigh-fmidrange,
795
            fhigh,
796
            int(_freqrange.txrangehi)/10))
797
        freqrange.append(rs)
798

    
799
        rs = RadioSetting("rxrangelow","RX Freq, Lower Limit (khz)", RadioSettingValueInteger(flow,
800
            flow+fmidrange,
801
            int(_freqrange.rxrangelow)/10))
802
        freqrange.append(rs)
803

    
804
        rs = RadioSetting("rxrangehi","RX Freq, Upper Limit (khz)", RadioSettingValueInteger(fhigh-fmidrange,
805
            fhigh,
806
            int(_freqrange.rxrangehi)/10))
807
        freqrange.append(rs)
808

    
809
        return settings
810

    
811
    def get_settings(self):
812
        try:
813
            return self._get_settings()
814
        except:
815
            import traceback
816
            LOG.error( "failed to parse settings")
817
            traceback.print_exc()
818
            return None
819

    
820
    def set_settings(self,settings):
821
        _settings = self._memobj.settings
822
        for element in settings:
823
            if not isinstance(element,RadioSetting):
824
                self.set_settings(element)
825
                continue
826
            else:
827
                try:
828
                    name = element.get_name()
829

    
830
                    if  name in ["txrangelow","txrangehi","rxrangelow","rxrangehi"]:
831
                        LOG.debug( "setting %s = %s" % (name,int(element.value)*10))
832
                        setattr(self._memobj.freqrange,name,int(element.value)*10)
833
                        continue
834

    
835
                    if name in ["startname"]:
836
                        LOG.debug( "setting %s = %s" % (name, element.value))
837
                        setattr(self._memobj.slabel,name,element.value)
838
                        continue
839

    
840
                    obj = _settings
841
                    setting = element.get_name()
842

    
843
                    if element.has_apply_callback():
844
                        LOG.debug( "using apply callback")
845
                        element.run_apply_callback()
846
                    else:
847
                        LOG.debug( "Setting %s = %s" % (setting, element.value))
848
                        setattr(obj, setting, element.value)
849
                except Exception, e:
850
                    LOG.debug( element.get_name())
851
                    raise
852

    
853
    @classmethod
854
    def match_model(cls, filedata, filename):
855
        return cls._file_ident in filedata[16:27]
856

    
857
@directory.register
858
class Th9000220Radio(Th9000Radio):
859
    """TYT TH-9000 220"""
860
    VENDOR = "TYT"    
861
    MODEL = "TH9000_220" 
862
    BAUD_RATE = 9600 
863
    _file_ident = "TH9000 220"
864
    valid_freq = [(220000000, 260000000)]
865

    
866
@directory.register
867
class Th9000144Radio(Th9000220Radio):
868
    """TYT TH-9000 144"""
869
    VENDOR = "TYT"    
870
    MODEL = "TH9000_144" 
871
    BAUD_RATE = 9600 
872
    _file_ident = "TH9000 144"
873
    valid_freq = [(138000000, 152000000)]
874

    
875
@directory.register
876
class Th9000440Radio(Th9000220Radio):
877
    """TYT TH-9000 440"""
878
    VENDOR = "TYT"    
879
    MODEL = "TH9000_440" 
880
    BAUD_RATE = 9600 
881
    _file_ident = "TH9000 440"
882
    valid_freq = [(400000000, 450000000)]
(9-9/14)