Project

General

Profile

New Model #1647 » id5100.py

b6db9284 - Dan Smith, 11/21/2022 12:14 AM

 
1
# Copyright 2022 Dan Smith <chirp@f.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
import logging
17
from textwrap import dedent
18

    
19
from chirp import chirp_common
20
from chirp import directory
21
from chirp.drivers import icf, id31
22
from chirp import bitwise
23

    
24
LOG = logging.getLogger(__name__)
25

    
26
LOG.info('Running module b6db9284')
27

    
28
MEM_FORMAT = """
29
struct {
30
  u24  mult1:3,
31
       mult2:3,
32
       freq:18;
33
  u16  offset;
34
  u16  rtone:6,
35
       ctone:6,
36
       unknown1:1,
37
       mode:3;
38
  u8   dtcs;
39
  u8   tuning_step:4,
40
       unknown2:4;
41
  u8   unknown3;
42
  u8   tmode:4,
43
       duplex:2,
44
       dtcs_polarity:2;
45
  char name[16];
46
  u8   dv_code;
47
  u8   urcall[7];
48
  u8   rpt1call[7];
49
  u8   rpt2call[7];
50
} memory[1004];
51

    
52
#seekto 0xC040;
53
u8 usedflags[125];
54
#seekto 0xC0BE;
55
u8 skipflags[125];
56
#seekto 0xC13B;
57
u8 pskipflags[125];
58

    
59
#seekto 0xC1C0;
60
struct {
61
  u8 bank;
62
  u8 index;
63
} bank_info[1000];
64

    
65
#seekto 0xC9C0;
66
char ICFComment[16];
67
struct {
68
  char name[16];
69
} bank_names[26];
70
"""
71

    
72
TUNE_STEPS = [5.0, 6.25, 8.33, 5.0, 10.0, 12.5, 15.0, 20.0, 25.0, 30.0, 50.0,
73
              5.0, 5.0, 5.0, 5.0]
74
MODES = ['FM', 'NFM', '2??', 'AM', 'NAM', 'DV', '6??', '7??']
75
TMODES = ['', 'Tone', '2??', 'TSQL', '4??', 'DTCS', 'TSQL-R', 'DTCS-R',
76
          'DTCS-T', 'Tone->DTCS', 'DTCS->Tone', 'Tone->Tone']
77
DUPLEX = ['', '-', '+']
78
DTCS_POL = ['NN', 'NR', 'RN', 'RR']
79
SPECIALS = ['144-C0', '144-C1', '430-C0', '430-C1']
80
MULTS = [5000, 6250, 25000 / 3.0]
81

    
82

    
83
def warp_byte_size(inbytes, obw=8, ibw=8):
84
    """Convert between "byte sizes".
85

    
86
    This will pack N-bit characters into a sequence of 8-bit bytes,
87
    and perform the opposite.
88

    
89
    ibw (input bit width) is the width of the storage
90
    obw (output bit width) is the width of the characters to extract
91

    
92
    ibw=8,obw=7 will pull seven-bit characters from a sequence of bytes
93
    ibw=7,obw=8 will pack seven-bit characters into a sequence of bytes
94
    """
95
    if isinstance(inbytes, str):
96
        inbytes = [ord(x) for x in inbytes]
97
    outbit = 0
98
    tmp = 0
99
    stripmask = 1 << (ibw - 1)
100
    for byte in inbytes:
101
        inbit = 0
102
        for i in range(0, max(obw, ibw - inbit)):
103
            if inbit == ibw:
104
                # Move to next char
105
                inbit = 0
106
                break
107
            tmp = (tmp << 1) | ((byte & stripmask) and 1 or 0)
108
            byte = (byte << 1) & 0xFF
109
            inbit += 1
110
            outbit += 1
111
            if outbit == obw:
112
                yield tmp
113
                tmp = 0
114
                outbit = 0
115

    
116

    
117
@directory.register
118
class ID5100Radio(icf.IcomRawCloneModeRadio,
119
                  chirp_common.IcomDstarSupport):
120
    VENDOR = "Icom"
121
    MODEL = "ID-5100"
122
    NEEDS_COMPAT_SERIAL = False
123

    
124
    _model = b'\x34\x84\x00\x01'
125
    _endframe = b"Icom Inc\x2E\xEE\xEE"
126

    
127
    # MapRev=1 Size is 260928 0x3FB40
128
    # MapRev=2 Size is 148160 0x242C0
129
    # MapRev=3 Size is 172928 0x2A380
130
    _memsize = 0x3FB40
131

    
132
    _ranges = [(0, _memsize, 64)]
133

    
134
    _num_banks = 26
135
    _bank_class = id31.ID31Bank
136

    
137
    _can_hispeed = True
138
    _icf_data = {
139
        'MapRev': 1,
140
        'EtcData': 404010,
141
    }
142

    
143
    def process_mmap(self):
144
        was_memsize = self._memsize
145

    
146
        # Apparently the 5100 has different reported memory sizes
147
        # depending on firmware version. When we're loading an image,
148
        # we should adjust our MapRev and _ranges to be correct for
149
        # saving ICF files and uploading. This also means we should
150
        # try to detect which version we are talking to and refuse to
151
        # upload an image to a radio with a mismatch.
152

    
153
        self._memsize = len(self._mmap)
154
        self._ranges = [(0, self._memsize, 64)]
155

    
156
        maprevs = {
157
            0x3FB40: 1,
158
            0x242C0: 2,
159
            0x2A380: 3,
160
        }
161

    
162
        if self._memsize != was_memsize:
163
            self._icf_data['MapRev'] = maprevs.get(self._memsize, 0)
164
            if self._icf_data['MapRev'] == 0:
165
                LOG.error('Unknown memsize %06X!', self._memsize)
166
                raise errors.InvalidDataError('Unsupported memory format!')
167
            LOG.info('Memory length changed from %06X to %06X; new MapRev=%i',
168
                     was_memsize, self._memsize, self._icf_data['MapRev'])
169
        else:
170
            LOG.debug('Unchanged memsize at %06X' % self._memsize)
171

    
172
        self._memobj = bitwise.parse(MEM_FORMAT, self._mmap)
173

    
174
    @classmethod
175
    def get_prompts(cls):
176
        rp = chirp_common.RadioPrompts()
177
        rp.info = dedent('This driver is NOT COMPLETE and does not '
178
                         'even support cloning with a cable. It is, '
179
                         'as yet, untested with real hardware and '
180
                         'should be considered basically radioactive.')
181
        rp.pre_download = rp.pre_upload = rp.info
182
        return rp
183

    
184
    def get_features(self):
185
        rf = chirp_common.RadioFeatures()
186
        rf.has_ctone = True
187
        rf.has_dtcs = True
188
        rf.has_dtcs_polarity = True
189
        rf.has_bank = True
190
        rf.has_bank_index = True
191
        rf.has_bank_names = True
192
        rf.requires_call_lists = False
193
        rf.valid_modes = [x for x in MODES
194
                          if '?' not in x]
195
        rf.valid_tmodes = [x for x in TMODES
196
                           if '-' not in x or '?' not in x]
197
        rf.memory_bounds = (0, 999)
198
        rf.valid_bands = [(118000000, 174000000),
199
                          (375000000, 550000000)]
200
        rf.valid_skips = ['', 'S', 'P']
201
        rf.valid_characters = chirp_common.CHARSET_ASCII
202
        rf.valid_name_length = 16
203
        rf.valid_tuning_steps = list(sorted(set(TUNE_STEPS)))
204
        rf.valid_special_chans = list(SPECIALS)
205
        return rf
206

    
207
    def _get_bank(self, loc):
208
        _bank = self._memobj.bank_info[loc]
209
        _bank.bank &= 0x1F
210
        if _bank.bank == 0x1F:
211
            return None
212
        else:
213
            return _bank.bank
214

    
215
    def _set_bank(self, loc, bank):
216
        _bank = self._memobj.bank_info[loc]
217
        if bank is None:
218
            _bank.bank = 0x1F
219
        else:
220
            _bank.bank = bank
221

    
222
    def _get_bank_index(self, loc):
223
        _bank = self._memobj.bank_info[loc]
224
        return _bank.index
225

    
226
    def _set_bank_index(self, loc, index):
227
        _bank = self._memobj.bank_info[loc]
228
        _bank.index = index
229

    
230
    def _get_raw_memory(self, number):
231
        if isinstance(number, str):
232
            number = 1000 + SPECIALS.index(number)
233
        return number, self._memobj.memory[number]
234

    
235
    def get_raw_memory(self, number):
236
        num, mem = self._get_raw_memory(number)
237
        return repr(mem)
238

    
239
    def get_memory(self, number):
240
        num, _mem = self._get_raw_memory(number)
241
        m = chirp_common.DVMemory()
242
        m.number = num
243
        if isinstance(number, str):
244
            m.extd_number = number
245
        else:
246
            _flg = self._memobj.usedflags[num // 8]
247
            _skp = self._memobj.skipflags[num // 8]
248
            _pskp = self._memobj.pskipflags[num // 8]
249
            m.empty = bool(_flg & (1 << num % 8))
250
            if _pskp & 1 << (num % 8):
251
                m.skip = 'P'
252
            elif _skp & 1 << (num % 8):
253
                m.skip = 'S'
254
            else:
255
                m.skip = ''
256

    
257
        mult = MULTS[_mem.mult1]
258
        m.freq = int(_mem.freq * mult)
259
        m.offset = int(_mem.offset * mult)
260
        m.tuning_step = TUNE_STEPS[_mem.tuning_step]
261
        m.name = str(_mem.name).rstrip()
262
        m.mode = MODES[_mem.mode]
263
        m.rtone = chirp_common.TONES[_mem.rtone]
264
        m.ctone = chirp_common.TONES[_mem.ctone]
265
        m.dtcs = chirp_common.DTCS_CODES[_mem.dtcs]
266
        m.dtcs_polarity = DTCS_POL[_mem.dtcs_polarity]
267
        tmode = TMODES[_mem.tmode]
268
        if '->' in tmode:
269
            m.tmode = 'Cross'
270
            m.cross_mode = tmode
271
        elif '-' in tmode and 0:
272
            # FIXME
273
            m.tmode, extra = tmode.split('-')
274
        else:
275
            m.tmode = tmode
276
        m.duplex = DUPLEX[_mem.duplex]
277

    
278
        m.dv_code = _mem.dv_code
279
        m.dv_urcall = ''.join(chr(x)
280
                              for x in warp_byte_size(_mem.urcall, 7, 8))
281
        m.dv_rpt1call = ''.join(chr(x)
282
                                for x in warp_byte_size(_mem.rpt1call, 7, 8))
283
        m.dv_rpt2call = ''.join(chr(x)
284
                                for x in warp_byte_size(_mem.rpt2call, 7, 8))
285

    
286
        return m
287

    
288
    def set_memory(self, mem):
289
        num, _mem = self._get_raw_memory(mem.number)
290
        if num < 1000:
291
            _flg = self._memobj.usedflags[num // 8]
292
            _skp = self._memobj.skipflags[num // 8]
293
            _pskp = self._memobj.pskipflags[num // 8]
294
            mybit = 1 << (num % 8)
295
            _flg |= mybit
296
            _skp &= ~mybit
297
            _pskp &= ~mybit
298

    
299
            if mem.empty:
300
                return
301

    
302
            _flg &= ~mybit
303
            if mem.skip == 'S':
304
                _skp |= mybit
305
            elif mem.skip == 'P':
306
                _pskp |= mybit
307

    
308
        if chirp_common.is_6_25(mem.freq):
309
            mult = MULTS[1]
310
        elif chirp_common.is_8_33(mem.freq):
311
            mult = MULTS[2]
312
        else:
313
            mult = MULTS[0]
314
        _mem.mult1 = _mem.mult2 = MULTS.index(mult)
315
        _mem.freq = mem.freq / mult
316
        _mem.offset = mem.offset / mult
317
        _mem.tuning_step = TUNE_STEPS.index(mem.tuning_step)
318
        _mem.name = mem.name.ljust(16)
319
        _mem.mode = MODES.index(mem.mode)
320
        _mem.rtone = chirp_common.TONES.index(mem.rtone)
321
        _mem.ctone = chirp_common.TONES.index(mem.ctone)
322
        _mem.dtcs = chirp_common.DTCS_CODES.index(mem.dtcs)
323
        _mem.dtcs_polarity = DTCS_POL.index(mem.dtcs_polarity)
324
        _mem.tmode = TMODES.index(mem.tmode)
325
        _mem.duplex = DUPLEX.index(mem.duplex)
326

    
327
        _mem.unknown1 = 0
328
        _mem.unknown2 = 0
329
        _mem.unknown3 = 0
330

    
331
        if isinstance(mem, chirp_common.DVMemory):
332
            _mem.dv_code = mem.dv_code
333
            _mem.urcall = list(
334
                warp_byte_size(mem.dv_urcall.ljust(8), 8, 7))
335
            _mem.rpt1call = list(
336
                warp_byte_size(mem.dv_rpt1call.ljust(8), 8, 7))
337
            _mem.rpt2call = list(
338
                warp_byte_size(mem.dv_rpt2call.ljust(8), 8, 7))
(17-17/38)