Project

General

Profile

New Model #1647 » id5100.py

47dec3fa495f43d457247f741c3a08a1ff2ead88 - Dan Smith, 11/19/2022 04:09 PM

 
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
22
from chirp import bitwise
23

    
24
LOG = logging.getLogger(__name__)
25

    
26

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

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

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

    
64
#seekto 0xC9D0;
65
struct {
66
  char name[16];
67
} bank_names[26];
68
"""
69

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

    
80

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

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

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

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

    
114

    
115
class ID5100Bank(icf.IcomNamedBank):
116
    def get_name(self):
117
        _banks = self._model._radio._memobj.bank_names
118
        return str(_banks[self.index].name).rstrip()
119

    
120
    def set_name(self, name):
121
        _banks = self._model._radio._memobj.bank_names
122
        _banks[self.index].name = name.ljust(16)[:16]
123

    
124

    
125
@directory.register
126
class ID5100Radio(icf.IcomRawCloneModeRadio,
127
                  chirp_common.IcomDstarSupport):
128
    VENDOR = "Icom"
129
    MODEL = "ID-5100"
130
    NEEDS_COMPAT_SERIAL = False
131

    
132
    _model = b'\x34\x84\x00\x01'
133
    _endframe = b"Icom Inc\x2E\xEE\xEE"
134
    _memsize = 0x2A380
135

    
136
    _ranges = [(0, _memsize, 32)]
137

    
138
    _num_banks = 26
139
    _bank_class = ID5100Bank
140

    
141
    _can_hispeed = True
142
    _icf_data = {
143
        'MapRev': 3,
144
        'EtcData': 404010,
145
    }
146

    
147
    @classmethod
148
    def get_prompts(cls):
149
        rp = chirp_common.RadioPrompts()
150
        rp.info = dedent('This driver is NOT COMPLETE and does not '
151
                         'even support cloning with a cable. It is, '
152
                         'as yet, untested with real hardware and '
153
                         'should be considered basically radioactive.')
154
        rp.pre_download = rp.pre_upload = rp.info
155
        return rp
156

    
157
    def get_features(self):
158
        rf = chirp_common.RadioFeatures()
159
        rf.has_ctone = True
160
        rf.has_dtcs = True
161
        rf.has_dtcs_polarity = True
162
        rf.has_bank = True
163
        rf.has_bank_index = True
164
        rf.has_bank_names = True
165
        rf.requires_call_lists = False
166
        rf.valid_modes = [x for x in MODES
167
                          if '?' not in x]
168
        rf.valid_tmodes = [x for x in TMODES
169
                           if '-' not in x or '?' not in x]
170
        rf.memory_bounds = (0, 999)
171
        rf.valid_bands = [(118000000, 174000000),
172
                          (375000000, 550000000)]
173
        rf.valid_skips = ['', 'S', 'P']
174
        rf.valid_characters = chirp_common.CHARSET_ASCII
175
        rf.valid_name_length = 16
176
        rf.valid_tuning_steps = list(sorted(set(TUNE_STEPS)))
177
        rf.valid_special_chans = list(SPECIALS)
178
        return rf
179

    
180
    def process_mmap(self):
181
        self._memobj = bitwise.parse(MEM_FORMAT, self._mmap)
182

    
183
    def _get_bank(self, loc):
184
        _bank = self._memobj.bank_info[loc]
185
        _bank.bank &= 0x1F
186
        if _bank.bank == 0x1F:
187
            return None
188
        else:
189
            return _bank.bank
190

    
191
    def _set_bank(self, loc, bank):
192
        _bank = self._memobj.bank_info[loc]
193
        if bank is None:
194
            _bank.bank = 0x1F
195
        else:
196
            _bank.bank = bank
197

    
198
    def _get_bank_index(self, loc):
199
        _bank = self._memobj.bank_info[loc]
200
        return _bank.index
201

    
202
    def _set_bank_index(self, loc, index):
203
        _bank = self._memobj.bank_info[loc]
204
        _bank.index = index
205

    
206
    def _get_raw_memory(self, number):
207
        if isinstance(number, str):
208
            number = 1000 + SPECIALS.index(number)
209
        return number, self._memobj.memory[number]
210

    
211
    def get_raw_memory(self, number):
212
        num, mem = self._get_raw_memory(number)
213
        return repr(mem)
214

    
215
    def get_memory(self, number):
216
        num, _mem = self._get_raw_memory(number)
217
        m = chirp_common.DVMemory()
218
        m.number = num
219
        if isinstance(number, str):
220
            m.extd_number = number
221
        else:
222
            _flg = self._memobj.usedflags[num // 8]
223
            _skp = self._memobj.skipflags[num // 8]
224
            _pskp = self._memobj.pskipflags[num // 8]
225
            m.empty = bool(_flg & (1 << num % 8))
226
            if _pskp & 1 << (num % 8):
227
                m.skip = 'P'
228
            elif _skp & 1 << (num % 8):
229
                m.skip = 'S'
230
            else:
231
                m.skip = ''
232

    
233
        mult = MULTS[_mem.mult1]
234
        m.freq = int(_mem.freq * mult)
235
        m.offset = int(_mem.offset * mult)
236
        m.tuning_step = TUNE_STEPS[_mem.tuning_step]
237
        m.name = str(_mem.name).rstrip()
238
        m.mode = MODES[_mem.mode]
239
        m.rtone = chirp_common.TONES[_mem.rtone]
240
        m.ctone = chirp_common.TONES[_mem.ctone]
241
        m.dtcs = chirp_common.DTCS_CODES[_mem.dtcs]
242
        m.dtcs_polarity = DTCS_POL[_mem.dtcs_polarity]
243
        tmode = TMODES[_mem.tmode]
244
        if '->' in tmode:
245
            m.tmode = 'Cross'
246
            m.cross_mode = tmode
247
        elif '-' in tmode and 0:
248
            # FIXME
249
            m.tmode, extra = tmode.split('-')
250
        else:
251
            m.tmode = tmode
252
        m.duplex = DUPLEX[_mem.duplex]
253

    
254
        m.dv_code = _mem.dv_code
255
        m.dv_urcall = ''.join(chr(x)
256
                              for x in warp_byte_size(_mem.urcall, 7, 8))
257
        m.dv_rpt1call = ''.join(chr(x)
258
                                for x in warp_byte_size(_mem.rpt1call, 7, 8))
259
        m.dv_rpt2call = ''.join(chr(x)
260
                                for x in warp_byte_size(_mem.rpt2call, 7, 8))
261

    
262
        return m
263

    
264
    def set_memory(self, mem):
265
        num, _mem = self._get_raw_memory(mem.number)
266
        if num < 1000:
267
            _flg = self._memobj.usedflags[num // 8]
268
            _skp = self._memobj.skipflags[num // 8]
269
            _pskp = self._memobj.pskipflags[num // 8]
270
            mybit = 1 << (num % 8)
271
            _flg |= mybit
272
            _skp &= ~mybit
273
            _pskp &= ~mybit
274

    
275
            if mem.empty:
276
                return
277

    
278
            _flg &= ~mybit
279
            if mem.skip == 'S':
280
                _skp |= mybit
281
            elif mem.skip == 'P':
282
                _pskp |= mybit
283

    
284
        if chirp_common.is_6_25(mem.freq):
285
            mult = MULTS[1]
286
        elif chirp_common.is_8_33(mem.freq):
287
            mult = MULTS[2]
288
        else:
289
            mult = MULTS[0]
290
        _mem.mult1 = _mem.mult2 = MULTS.index(mult)
291
        _mem.freq = mem.freq / mult
292
        _mem.offset = mem.offset / mult
293
        _mem.tuning_step = TUNE_STEPS.index(mem.tuning_step)
294
        _mem.name = mem.name.ljust(16)
295
        _mem.mode = MODES.index(mem.mode)
296
        _mem.rtone = chirp_common.TONES.index(mem.rtone)
297
        _mem.ctone = chirp_common.TONES.index(mem.ctone)
298
        _mem.dtcs = chirp_common.DTCS_CODES.index(mem.dtcs)
299
        _mem.dtcs_polarity = DTCS_POL.index(mem.dtcs_polarity)
300
        _mem.tmode = TMODES.index(mem.tmode)
301
        _mem.duplex = DUPLEX.index(mem.duplex)
302

    
303
        _mem.unknown1 = 0
304
        _mem.unknown2 = 0
305
        _mem.unknown3 = 0
306

    
307
        if isinstance(mem, chirp_common.DVMemory):
308
            _mem.dv_code = mem.dv_code
309
            _mem.urcall = list(
310
                warp_byte_size(mem.dv_urcall.ljust(8), 8, 7))
311
            _mem.rpt1call = list(
312
                warp_byte_size(mem.dv_rpt1call.ljust(8), 8, 7))
313
            _mem.rpt2call = list(
314
                warp_byte_size(mem.dv_rpt2call.ljust(8), 8, 7))
(3-3/38)