Project

General

Profile

New Model #1035 » tytpatch9000-001.patch

David Fannin, 09/28/2013 11:23 PM

View differences:

/dev/null Thu Jan 01 00:00:00 1970 +0000 → chirp/th9000.py Sat Sep 28 23:18:07 2013 -0700
# Copyright 2013 Dan Smith <dsmith@danplanet.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import os
import struct
import time
from chirp import bitwise
from chirp import chirp_common
from chirp import directory
from chirp import errors
from chirp import memmap
from chirp import util
"""
Description: Chirp Data Structure of the Memory Map for TYT TH-9000/VHF 2Meter 144-148Mhz Radio
Version: 0.6
Date: 9/28/2013
Status: Imcomplete
- This is the initial, incomplete version of the channel data structure.
- The memory map looks a lot like the Anytone 5888 model, however, these does appear to be significant differences.
Overall Memory Map:
Memory Map (Range 0x0100-3FF0, step 0x10):
Field Start End Size
(hex) (hex) (hex)
1 Channel Set Flag 0100 011F 20
2 Channel Skip Flag 0120 013F 20
3 Blank/Unknown 0140 01EF B0
4 Unknown 01F0 01FF 10
5 TX/RX Range 0200 020F 10
6 Bootup Passwd 0210 021F 10
7 Options, Radio 0220 023F 20
8 Unknown 0240 019F
9 Channel Bank 2000 38FF 1900
Channel 000 2000 201F 20
Channel 001 2020 202F 20
...
Channel 199 38E0 38FF 20
10 Blank/Unknown 3900 3FFF 6FF 14592 16383 1792
Total Map Size 16128 (2^8 = 16384)
Protocol for Upload Sequence, Only Writes shown:
1) Start
"Program" 50 52 4F 47 52 41 4D
0x02 02
"R\0x01\0x40\x10" 52 01 40 10 (?means "Read" address 1040, 16 bytes, but doesn't match write data field)
2) Write Records, Loop from address 0100 to 3900, step 0x10
"W"<address><length><data><checksum>"\0x06" 57 .. .. 10 ..(*16) .. 06 (write record of 16 bytes)
header[4 bytes]:
command[1] = Write ("W")
address[2] = 0100 to 3900
length[1] = 0x10 (16 bytes length)
data[16 bytes]:
data[16] = 16 byte payload
end[2 bytes]:
checksum[1] = 1 byte, uses digit check checksum ( sum of header and data bytes, modulo 256)
end of record[1] = 0x06 (ascii ACK char)
3) END
"END" 45 4E 44
"""
"""
TH9000/VHF memory map
section: 1 and 2: Channel Set/Skip Flags
Channel Set (starts 0x100) : Channel Set bit is value 0 if a memory location in the channel bank is active.
Channel Skip (starts 0x120): Channel Skip bit is value 0 if a memory location in the channel bank is active.
Both flag maps are a total 24 bytes in length, aligned on 32 byte records.
bit = 0 channel set/no skip, 1 is channel not set/skip
to index a channel:
cbyte = channel / 8 ;
cbit = channel % 8 ;
setflag = csetflag[cbyte].c[cbit] ;
skipflag = cskipflag[cbyte].c[cbit] ;
channel range is 0-199, range is 32 bytes (last 7 unknown)
"""
mem_format = """
#seekto 0x0100;
struct {
bit c[8];
} csetflag[24];
struct {
u8 unknown0100[7] ;
}
#seekto 0x0120
struct {
bit c[8];
} cskipflag[24] ;
struct {
u8 unknown0120[7] ;
}
"""
"""
TH9000/VHF memory map
section: 5 TX/RX Range
used to set the TX/RX range of the radio (e.g. 144-148Mhz for 2 meter)
possible to set range to 136-172Mhz for tx/rx
"""
mem_format = """
#seekto 0x0200;
struct {
bbcd txrangelow[4];
bbcd txrangehi[4];
bbcd rxrangelow[4];
bbcd rxrangehi[4];
}
"""
"""
TH9000/VHF memory map
section: 6 bootup_passwd
used to set bootup passwd (see boot_passwd checkbox option)
options - bootup password
bytes:bit type description
---------------------------------------------------------------------------
6 u8 bootup_passwd[6] bootup passwd, 6 chars, numberic chars 30-39 , see boot_passwd checkbox to set
10 u8 unknown;
"""
mem_format = mem_format + """
#seekto 0x0210
struct {
u8 bootup_passwd[6];
u8 unknown2010[10];
};
"""
"""
TH9000/VHF memory map
section: 7 Radio Options
used to set a number of radio options
bytes:bit type description
---------------------------------------------------------------------------
1 u8 display_mode display mode, range 0-2, 0=freq,1=channel,2=name (selecting name affects vfo_mr)
1 u8 vfo_mr; vfo_mr , 0=vfo, mr=1
1 u8 unknown;
1 u8 squelch; squelch level, range 0-19, hex for menu
1 u8 unknown[2];
1 u8 channel_lock; if display_mode[channel] selected, then lock=1,no lock =0
1 u8 unknown;
1 u8 bg_brightness ; background brightness, range 0-21, hex, menu index
1 u8 unknown;
1 u8 bg_color ; bg color, menu index, blue 0 , orange 1, purple 2
1 u8 tbst_freq ; tbst freq , menu 0 = 1750Hz, 1=2100 , 2=1000 , 3=1450hz
1 u8 timeout_timer; timeout timer, hex, value = minutes, 0= no timeout
1 u8 unknown;
1 u8 auto_power_off; auto power off, range 0-3, off,30min, 1hr, 2hr, hex menu index
1 u8 voice_prompt; voice prompt, value 0,1 , Beep ON = 1, Beep Off = 2
"""
mem_format = mem_format + """
#seekto 0x0220
struct {
u8 display_mode;
u8 vfo_mr;
u8 unknown0220A;
u8 squelch;
u8 unknown0220B[2];
u8 channel_lock;
u8 unknown0220C;
u8 bg_brightness;
u8 unknown0220D;
u8 bg_color;
u8 tbst_freq;
u8 timeout_timer;
u8 unknown0220E;
u8 auto_power_off;
u8 voice_prompt;
};
"""
"""
description of function setup options, starting at 0x0230
bytes:bit type description
---------------------------------------------------------------------------
1 u8 // 0
:4 unknown:6
:1 elim_sql_tail:1 eliminate squelsh tail when no ctcss checkbox (1=checked)
:1 sql_key_function "squelch off" 1 , "squelch momentary off" 0 , menu index
2 u8 unknown[2] /1-2
1 u8 // 3
:4 unknown:4
:1 inhibit_init_ops:1 //bit 5
:1 unknownD:1
:1 inhibit_setup_bg_chk:1 //bit 7
:1 unknown:1
1 u8 tail_elim_type menu , (off=0,120=1,180=2), // 4
1 u8 choose_tx_power menu , (60w=0,25w=1) // 5
2 u8 unknown[2]; // 6-7
1 u8 bootup_passwd_flag checkbox 1=on, 0=off // 8
7 u8 unknown[7]; // 9-F
"""
mem_format = mem_format + """
#seekto 0x0230
struct {
u8 unknown0230A:6,
elim_sql_tail:1,
sql_key_function:1;
u8 unknown0230B[2];
u8 unknown0230C:4,
inhibit_init_ops:1,
unknown0230D:1,
inhibit_setup_bg_chk,
unknown0230E:1;
u8 tail_elim_type;
u8 choose_tx_power;
u8 unknown0230F[2];
u8 bootup_passwd_flag;
u8 unknown0230G[7];
};
"""
"""
TH9000/VHF memory map
section: 9 Channel Bank
description of channel bank (200 channels , range 0-199)
Each 32 Byte (0x20 hex) record:
bytes:bit type description
---------------------------------------------------------------------------
4 bbcd freq[4] receive frequency in packed binary coded decimal
4 bbcd offset[4] transmit offset in packed binary coded decimal (note: plus/minus direction set by 'duplex' field)
1 u8
:4 unknown:4
:4 tune_step:4 tuning step, menu index value from 0-9
1 u8
:4 unknown:4 not yet decoded, used for DCS coding?
:2 channel_width:2 channel spacing, menu index value from 0-3
:1 reverse:1 reverse flag, 0=off, 1=on (reverses tx and rx freqs)
:1 txoff:1 transmitt off flag, 0=transmit , 1=do not transmit
1 u8
:1 talkaround:1 talkaround flag, 0=off, 1=on (bypasses repeater)
:1 compander:1 compander flag, 0=off, 1=on (turns on/off voice compander option)
:2 unknown:2
:2 power:2 tx power setting, value range 0-2, 0=hi,1=med,2=lo
:2 duplex:2 duplex settings, 0=simplex,1= minus(-) offset, 2= plus (+) offset (see offset field)
1 u8
:4 unknown:4
:2 rxtmode:2 rx tone mode, value range 0-2, 0=none, 1=CTCSS, 2=DCS (ctcss tone in field rxtone)
:2 txtmode:2 tx tone mode, value range 0-2, 0=none, 1=CTCSS, 3=DCS (ctcss tone in field txtone)
1 u8
:2 unknown:2
:6 txtone:6 tx ctcss tone, menu index
1 u8
:2 unknown:2
:6 rxtone:6 rx ctcss tone, menu index
1 u8 txcode ?, not used for ctcss
1 u8 rxcode ?, not used for ctcss
3 u8 unknown[3]
7 char name[7] 7 byte char string for channel name
1 u8
:6 unknown:6,
:2 busychannellockout:2 busy channel lockout option , 0=off, 1=repeater, 2=busy (lock out tx if channel busy)
4 u8 unknownI[4];
1 u8
:7 unknown:7
:1 scrambler:1 scrambler flag, 0=off, 1=on (turns on tyt scrambler option)
"""
mem_format = mem_format + """
#seekto 0x2000;
struct memory {
bbcd freq[4];
bbcd offset[4];
u8 unknown2000A:4,
tune_step:4;
u8 unknown2000B:4,
channel_width:2,
reverse:1,
txoff:1;
u8 talkaround:1,
compander:1,
unknown2000C:2,
power:2,
duplex:2;
u8 unknown2000D:4,
rxtmode:2,
txtmode:2;
u8 unknown2000E:2,
txtone:6;
u8 unknown2000F:2,
rxtone:6;
u8 txcode;
u8 rxcode;
u8 unknown2000G[3];
char name[7];
u8 unknown2000H:6,
busychannellockout:2;
u8 unknown2000I[4];
u8 unknown2000J:7,
scrambler:1;
};
struct memory memory[200] ;
"""
def _debug(string):
if "CHIRP_DEBUG" in os.environ or True:
print string
def _echo_write(radio, data):
try:
radio.pipe.write(data)
radio.pipe.read(len(data))
except Exception, e:
print "Error writing to radio: %s" % e
raise errors.RadioError("Unable to write to radio")
def _read(radio, length):
try:
data = radio.pipe.read(length)
except Exception, e:
print "Error reading from radio: %s" % e
raise errors.RadioError("Unable to read from radio")
if len(data) != length:
print "Short read from radio (%i, expected %i)" % (len(data),
length)
print util.hexprint(data)
raise errors.RadioError("Short read from radio")
return data
def _ident(radio):
radio.pipe.setTimeout(1)
_echo_write(radio, "PROGRAM")
response = radio.pipe.read(3)
if response != "QX\x06":
print "Response was:\n%s" % util.hexprint(response)
raise errors.RadioError("Unsupported model")
_echo_write(radio, "\x02")
response = radio.pipe.read(16)
_debug(util.hexprint(response))
if response[1:8] != "ITH-9000":
print "Response was:\n%s" % util.hexprint(response)
raise errors.RadioError("Unsupported model")
def _finish(radio):
endframe = "\x45\x4E\x44"
_echo_write(radio, endframe)
result = radio.pipe.read(1)
if result != "\x06":
print "Got:\n%s" % util.hexprint(result)
raise errors.RadioError("Radio did not finish cleanly")
def _checksum(data):
cs = 0
for byte in data:
cs += ord(byte)
return cs % 256
def _send(radio, cmd, addr, length, data=None):
frame = struct.pack(">cHb", cmd, addr, length)
if data:
frame += data
frame += chr(_checksum(frame[1:]))
frame += "\x06"
_echo_write(radio, frame)
_debug("Sent:\n%s" % util.hexprint(frame))
if data:
result = radio.pipe.read(1)
if result != "\x06":
print "Ack was: %s" % repr(result)
raise errors.RadioError("Radio did not accept block at %04x" % addr)
return
result = _read(radio, length + 6)
_debug("Got:\n%s" % util.hexprint(result))
header = result[0:4]
data = result[4:-2]
ack = result[-1]
if ack != "\x06":
print "Ack was: %s" % repr(ack)
raise errors.RadioError("Radio NAK'd block at %04x" % addr)
_cmd, _addr, _length = struct.unpack(">cHb", header)
if _addr != addr or _length != _length:
print "Expected/Received:"
print " Length: %02x/%02x" % (length, _length)
print " Addr: %04x/%04x" % (addr, _addr)
raise errors.RadioError("Radio send unexpected block")
cs = _checksum(result[1:-2])
if cs != ord(result[-2]):
print "Calculated: %02x" % cs
print "Actual: %02x" % ord(result[-2])
raise errors.RadioError("Block at 0x%04x failed checksum" % addr)
return data
def _download(radio):
_ident(radio)
memobj = None
data = ""
for start, end in radio._ranges:
for addr in range(start, end, 0x10):
block = _send(radio, 'R', addr, 0x10)
data += block
status = chirp_common.Status()
status.cur = len(data)
status.max = end
status.msg = "Cloning from radio"
radio.status_fn(status)
memobj = bitwise.parse(_mem_format, data)
_finish(radio)
return memmap.MemoryMap(data)
def _upload(radio):
_ident(radio)
for start, end in radio._ranges:
for addr in range(start, end, 0x10):
block = radio._mmap[addr:addr + 0x10]
_send(radio, 'W', addr, len(block), block)
status = chirp_common.Status()
status.cur = addr
status.max = end
status.msg = "Cloning to radio"
radio.status_fn(status)
_finish(radio)
@directory.register
class TytTh9000VHFRadio(chirp_common.CloneModeRadio,
chirp_common.ExperimentalRadio):
"""TYT TH-9000 VHF"""
VENDOR = "TYT"
MODEL = "TH9000"
BAUD_RATE = 9600
# May try to mirror the OEM behavior later
_ranges = [
(0x0100, 0x3FF0),
]
@classmethod
def get_experimental_warning(cls):
return "FOO"
def get_features(self):
rf = chirp_common.RadioFeatures()
rf.valid_bands = [(136000000)]
rf.valid_characters = chirp_common.CHARSET_UPPER_NUMERIC
rf.valid_name_length = 7
rf.memory_bounds = (0, 199)
return rf
@classmethod
def match_model(cls, filedata, filename):
return filedata[0x21:0x28] == "ITH-9000"
(2-2/14)