Project

General

Profile

Bug #621 » vxa700.py

Modified VXA-700 driver to ignore the invalid bit - Dan Smith, 02/25/2013 04:28 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 3 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
from chirp import chirp_common, util, directory, memmap, errors
17
from chirp import bitwise
18

    
19
import time
20
import struct
21

    
22
def _debug(string):
23
    pass
24
    print string
25

    
26
def _send(radio, data):
27
    _debug("Sending %s" % repr(data))
28
    radio.pipe.write(data)
29
    radio.pipe.flush()
30
    echo = radio.pipe.read(len(data))
31
    if len(echo) != len(data):
32
        raise errors.RadioError("Invalid echo")
33

    
34
def _spoonfeed(radio, data):
35
    #count = 0
36
    _debug("Writing %i:\n%s" % (len(data), util.hexprint(data)))
37
    for byte in data:
38
        radio.pipe.write(byte)
39
        radio.pipe.flush()
40
        time.sleep(0.01)
41
        continue
42
        # This is really unreliable for some reason,
43
        # so just blindly send the data
44
        echo = radio.pipe.read(1)
45
        if echo != byte:
46
            print "%02x != %02x" % (ord(echo), ord(byte))
47
            raise errors.RadioError("No echo?")
48
        #count += 1
49

    
50
def _download(radio):
51
    count = 0
52
    data = ""
53
    while len(data) < radio.get_memsize():
54
        count += 1
55
        chunk = radio.pipe.read(133)
56
        if len(chunk) == 0 and len(data) == 0 and count < 30:
57
            continue
58
        if len(chunk) != 132:
59
            raise errors.RadioError("Got short block (length %i)" % len(chunk))
60

    
61
        checksum = ord(chunk[-1])
62
        _flag, _length, _block, _data, checksum = \
63
            struct.unpack("BBB128sB", chunk)
64

    
65
        cs = 0
66
        for byte in chunk[:-1]:
67
            cs += ord(byte)
68
        if (cs % 256) != checksum:
69
            raise errors.RadioError("Invalid checksum at 0x%02x" % len(data))
70

    
71
        data += _data
72
        _send(radio, "\x06")
73

    
74
        if radio.status_fn:
75
            status = chirp_common.Status()
76
            status.msg = "Cloning from radio"
77
            status.cur = len(data)
78
            status.max = radio.get_memsize()
79
            radio.status_fn(status)
80

    
81
    return memmap.MemoryMap(data)
82

    
83
def _upload(radio):
84
    for i in range(0, radio.get_memsize(), 128):
85
        chunk = radio.get_mmap()[i:i+128]
86
        cs = 0x20 + 130 + (i / 128)
87
        for byte in chunk:
88
            cs += ord(byte)
89
        _spoonfeed(radio,
90
                   struct.pack("BBB128sB",
91
                               0x20,
92
                               130,
93
                               i / 128,
94
                               chunk,
95
                               cs % 256))
96
        radio.pipe.write("")
97
        # This is really unreliable for some reason, so just
98
        # blindly proceed
99
        # ack = radio.pipe.read(1)
100
        ack = "\x06"
101
        time.sleep(0.5)
102
        if ack != "\x06":
103
            print repr(ack)
104
            raise errors.RadioError("Radio did not ack block %i" % (i / 132))
105
        #radio.pipe.read(1)
106
        if radio.status_fn:
107
            status = chirp_common.Status()
108
            status.msg = "Cloning to radio"
109
            status.cur = i
110
            status.max = radio.get_memsize()
111
            radio.status_fn(status)
112

    
113
MEM_FORMAT = """
114
struct memory_struct {
115
  u8 unknown1;
116
  u8 unknown2:2,
117
     isfm:1,
118
     power:2,
119
     step:3;
120
  u8 unknown5:2,
121
     showname:1,
122
     skip:1,
123
     duplex:2,
124
     unknown6:2;
125
  u8 tmode:2,
126
     unknown7:6;
127
  u8 unknown8;
128
  u8 unknown9:2,
129
     tone:6;
130
  u8 dtcs;
131
  u8 name[8];
132
  u16 freq;
133
  u8 offset;
134
};
135

    
136
u8 headerbytes[6];
137

    
138
#seekto 0x0006;
139
u8 invisible_bits[13];
140
u8 bitfield_pad[3];
141
u8 invalid_bits[13];
142

    
143
#seekto 0x017F;
144
struct memory_struct memory[100];
145
"""
146

    
147
CHARSET = "".join(["%i" % i for i in range(0, 10)]) + \
148
    "".join([chr(ord("A") + i) for i in range(0, 26)]) + \
149
    "".join([chr(ord("a") + i) for i in range(0,26)]) + \
150
    "., :;!\"#$%&'()*+-/=<>?@[?]^_`{|}????~??????????????????????????"
151
            
152
TMODES = ["", "Tone", "TSQL", "DTCS"]
153
DUPLEX = ["", "-", "+", ""]
154
POWER = [chirp_common.PowerLevel("Low1", watts=0.050),
155
         chirp_common.PowerLevel("Low2", watts=1.000),
156
         chirp_common.PowerLevel("Low3", watts=2.500),
157
         chirp_common.PowerLevel("High", watts=5.000)]
158

    
159
def _wipe_memory(_mem):
160
    _mem.set_raw("\x00" * (_mem.size() / 8))
161

    
162
@directory.register
163
class VXA700Radio(chirp_common.CloneModeRadio):
164
    """Vertex Standard VXA-700"""
165
    VENDOR = "Vertex Standard"
166
    MODEL = "VXA-700"
167
    _memsize = 4096
168

    
169
    def sync_in(self):
170
        try:
171
            self.pipe.setTimeout(2)
172
            self._mmap = _download(self)
173
        except errors.RadioError:
174
            raise
175
        except Exception, e:
176
            raise errors.RadioError("Failed to communicate " +
177
                                    "with the radio: %s" % e)
178
        self.process_mmap()
179

    
180
    def sync_out(self):
181
        #header[4] = 0x00 <- default
182
        #            0xFF <- air band only
183
        #            0x01 <- air band only
184
        #            0x02 <- air band only
185
        try:
186
            self.pipe.setTimeout(2)
187
            _upload(self)
188
        except errors.RadioError:
189
            raise
190
        except Exception, e:
191
            raise errors.RadioError("Failed to communicate " +
192
                                    "with the radio: %s" % e)
193

    
194
    def process_mmap(self):
195
        self._memobj = bitwise.parse(MEM_FORMAT, self._mmap)
196

    
197
    def get_features(self):
198
        rf = chirp_common.RadioFeatures()
199
        rf.has_bank = False
200
        rf.has_ctone = False
201
        rf.has_dtcs_polarity = False
202
        rf.has_tuning_step = False
203
        rf.valid_tmodes = TMODES
204
        rf.valid_name_length = 8
205
        rf.valid_characters = CHARSET
206
        rf.valid_skips = ["", "S"]
207
        rf.valid_bands = [(88000000, 165000000)]
208
        rf.valid_tuning_steps = [5.0, 10.0, 12.5, 15.0, 20.0, 25.0, 50.0, 100.0]
209
        rf.valid_modes = ["AM", "FM"]
210
        rf.valid_power_levels = POWER
211
        rf.memory_bounds = (1, 100)
212
        return rf
213

    
214
    def _get_mem(self, number):
215
        return self._memobj.memory[number - 1]
216

    
217
    def get_raw_memory(self, number):
218
        _mem = self._get_mem(number)
219
        return repr(_mem) + util.hexprint(_mem.get_raw())
220

    
221
    def get_memory(self, number):
222
        _mem = self._get_mem(number)
223
        byte = (number - 1) / 8
224
        bit = 1 << ((number - 1) % 8)
225

    
226
        mem = chirp_common.Memory()
227
        mem.number = number
228

    
229
        if number == 20:
230
            print "invi: %s inva: %s" % (
231
                self._memobj.invisible_bits[byte] & bit,
232
                self._memobj.invalid_bits[byte] & bit,
233
                )
234

    
235
        if self._memobj.invisible_bits[byte] & bit:
236
            mem.empty = True
237
            if self._memobj.invalid_bits[byte] & bit:
238
                return mem
239

    
240
        if _mem.step & 0x05: # Not sure this is right, but it seems to be
241
            mult = 6250
242
        else:
243
            mult = 5000
244

    
245
        mem.freq = int(_mem.freq) * mult
246
        mem.rtone = chirp_common.TONES[_mem.tone]
247
        mem.dtcs = chirp_common.DTCS_CODES[_mem.dtcs]
248
        mem.tmode = TMODES[_mem.tmode]
249
        mem.duplex = DUPLEX[_mem.duplex]
250
        mem.offset = int(_mem.offset) * 5000 * 10
251
        mem.mode = _mem.isfm and "FM" or "AM"
252
        mem.skip = _mem.skip and "S" or ""
253
        mem.power = POWER[_mem.power]
254

    
255
        for char in _mem.name:
256
            try:
257
                mem.name += CHARSET[char]
258
            except IndexError:
259
                break
260
        mem.name = mem.name.rstrip()
261

    
262
        return mem
263

    
264
    def set_memory(self, mem):
265
        _mem = self._get_mem(mem.number)
266
        byte = (mem.number - 1) / 8
267
        bit = 1 << ((mem.number - 1) % 8)
268

    
269
        if mem.empty and self._memobj.invisible_bits[byte] & bit:
270
            self._memobj.invalid_bits[byte] |= bit
271
            return
272
        if mem.empty:
273
            self._memobj.invisible_bits[byte] |= bit
274
            return
275

    
276
        if self._memobj.invalid_bits[byte] & bit:
277
            _wipe_memory(_mem)
278

    
279
        self._memobj.invisible_bits[byte] &= ~bit
280
        self._memobj.invalid_bits[byte] &= ~bit
281

    
282
        _mem.unknown2 = 0x02 # Channels don't display without this
283
        _mem.unknown7 = 0x01 # some bit in this field is related to
284
        _mem.unknown8 = 0xFF # being able to transmit
285

    
286
        # HACK: testing
287
        #_mem.unknown7 = 0x2F
288

    
289
        if chirp_common.required_step(mem.freq) == 12.5:
290
            mult = 6250
291
            _mem.step = 0x05
292
        else:
293
            mult = 5000
294
            _mem.step = 0x00
295

    
296
        _mem.freq = mem.freq / mult
297
        _mem.tone = chirp_common.TONES.index(mem.rtone)
298
        _mem.dtcs = chirp_common.DTCS_CODES.index(mem.dtcs)
299
        _mem.tmode = TMODES.index(mem.tmode)
300
        _mem.duplex = DUPLEX.index(mem.duplex)
301
        _mem.offset = mem.offset / 5000 / 10
302
        _mem.isfm = mem.mode == "FM"
303
        _mem.skip = mem.skip == "S"
304
        try:
305
            _mem.power = POWER.index(mem.power)
306
        except ValueError:
307
            _mem.power = 3 # High
308

    
309
        for i in range(0, 8):
310
            try:
311
                _mem.name[i] = CHARSET.index(mem.name[i])
312
            except IndexError:
313
                _mem.name[i] = 0x40
314
        _mem.showname = bool(mem.name.strip())
315

    
316
    @classmethod
317
    def match_model(cls, filedata, filename):
318
        return len(filedata) == cls._memsize and \
319
            ord(filedata[5]) == 0x0F
(3-3/4)