Project

General

Profile

New Model #1647 » id5100.py

12ab9f9c24ea3f3938a3731dde4ebec73df09cd4 - Dan Smith, 11/18/2022 12:17 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
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

    
59
TUNE_STEPS = [5.0, 6.25, 8.33, 5.0, 10.0, 12.5, 15.0, 20.0, 25.0, 30.0, 50.0,
60
              5.0, 5.0, 5.0, 5.0]
61
MODES = ['FM', 'NFM', '2??', 'AM', 'NAM', 'DV', '6??', '7??']
62
TMODES = ['', 'Tone', '2??', 'TSQL', '4??', 'DTCS', 'TSQL-R', 'DTCS-R',
63
          'DTCS-T', 'Tone->DTCS', 'DTCS->Tone', 'Tone->Tone']
64
DUPLEX = ['', '-', '+']
65
DTCS_POL = ['NN', 'NR', 'RN', 'RR']
66
SPECIALS = ['144-C0', '144-C1', '430-C0', '430-C1']
67
MULTS = [5000, 6250, 25000 / 3.0]
68

    
69

    
70
def warp_byte_size(inbytes, obw=8, ibw=8):
71
    """Convert between "byte sizes".
72

    
73
    This will pack N-bit characters into a sequence of 8-bit bytes,
74
    and perform the opposite.
75

    
76
    ibw (input bit width) is the width of the storage
77
    obw (output bit width) is the width of the characters to extract
78

    
79
    ibw=8,obw=7 will pull seven-bit characters from a sequence of bytes
80
    ibw=7,obw=8 will pack seven-bit characters into a sequence of bytes
81
    """
82
    if isinstance(inbytes, str):
83
        inbytes = [ord(x) for x in inbytes]
84
    outbit = 0
85
    tmp = 0
86
    stripmask = 1 << (ibw - 1)
87
    for byte in inbytes:
88
        inbit = 0
89
        for i in range(0, max(obw, ibw - inbit)):
90
            if inbit == ibw:
91
                # Move to next char
92
                inbit = 0
93
                break
94
            tmp = (tmp << 1) | ((byte & stripmask) and 1 or 0)
95
            byte = (byte << 1) & 0xFF
96
            inbit += 1
97
            outbit += 1
98
            if outbit == obw:
99
                yield tmp
100
                tmp = 0
101
                outbit = 0
102

    
103

    
104
@directory.register
105
class ID5100Radio(icf.IcomRawCloneModeRadio,
106
                  chirp_common.IcomDstarSupport):
107
    VENDOR = "Icom"
108
    MODEL = "ID-5100"
109
    NEEDS_COMPAT_SERIAL = False
110

    
111
    _model = b'\x34\x84\x00\x01'
112
    _memsize = 172928
113

    
114
    _ranges = [(0, _memsize, 32)]
115

    
116
    _can_hispeed = True
117
    _icf_maprev = 3
118
    _icf_etcdata = '404010'
119

    
120
    @classmethod
121
    def get_prompts(cls):
122
        rp = chirp_common.RadioPrompts()
123
        rp.info = dedent('This driver is NOT COMPLETE and does not '
124
                         'even support cloning with a cable. It is, '
125
                         'as yet, untested with real hardware and '
126
                         'should be considered basically radioactive.')
127
        rp.pre_download = rp.pre_upload = rp.info
128
        return rp
129

    
130
    def get_features(self):
131
        rf = chirp_common.RadioFeatures()
132
        rf.has_ctone = True
133
        rf.has_dtcs = True
134
        rf.has_dtcs_polarity = True
135
        rf.has_bank = False
136
        rf.requires_call_lists = False
137
        rf.valid_modes = [x for x in MODES
138
                          if '?' not in x]
139
        rf.valid_tmodes = [x for x in TMODES
140
                           if '-' not in x or '?' not in x]
141
        rf.memory_bounds = (0, 999)
142
        rf.valid_bands = [(118000000, 174000000),
143
                          (375000000, 550000000)]
144
        rf.valid_skips = ['', 'S', 'P']
145
        rf.valid_characters = chirp_common.CHARSET_ASCII
146
        rf.valid_name_length = 16
147
        rf.valid_tuning_steps = list(sorted(set(TUNE_STEPS)))
148
        rf.valid_special_chans = list(SPECIALS)
149
        return rf
150

    
151
    def process_mmap(self):
152
        self._memobj = bitwise.parse(MEM_FORMAT, self._mmap)
153

    
154
    def _get_raw_memory(self, number):
155
        if isinstance(number, str):
156
            number = 1000 + SPECIALS.index(number)
157
        return number, self._memobj.memory[number]
158

    
159
    def get_raw_memory(self, number):
160
        num, mem = self._get_raw_memory(number)
161
        return repr(mem)
162

    
163
    def get_memory(self, number):
164
        num, _mem = self._get_raw_memory(number)
165
        m = chirp_common.DVMemory()
166
        m.number = num
167
        if isinstance(number, str):
168
            m.extd_number = number
169
        else:
170
            _flg = self._memobj.usedflags[num // 8]
171
            _skp = self._memobj.skipflags[num // 8]
172
            _pskp = self._memobj.pskipflags[num // 8]
173
            m.empty = bool(_flg & (1 << num % 8))
174
            if _pskp & 1 << (num % 8):
175
                m.skip = 'P'
176
            elif _skp & 1 << (num % 8):
177
                m.skip = 'S'
178
            else:
179
                m.skip = ''
180

    
181
        mult = MULTS[_mem.mult1]
182
        m.freq = int(_mem.freq * mult)
183
        m.offset = int(_mem.offset * mult)
184
        m.tuning_step = TUNE_STEPS[_mem.tuning_step]
185
        m.name = str(_mem.name).rstrip()
186
        m.mode = MODES[_mem.mode]
187
        m.rtone = chirp_common.TONES[_mem.rtone]
188
        m.ctone = chirp_common.TONES[_mem.ctone]
189
        m.dtcs = chirp_common.DTCS_CODES[_mem.dtcs]
190
        m.dtcs_polarity = DTCS_POL[_mem.dtcs_polarity]
191
        tmode = TMODES[_mem.tmode]
192
        if '->' in tmode:
193
            m.tmode = 'Cross'
194
            m.cross_mode = tmode
195
        elif '-' in tmode and 0:
196
            # FIXME
197
            m.tmode, extra = tmode.split('-')
198
        else:
199
            m.tmode = tmode
200
        m.duplex = DUPLEX[_mem.duplex]
201

    
202
        m.dv_code = _mem.dv_code
203
        m.dv_urcall = ''.join(chr(x)
204
                              for x in warp_byte_size(_mem.urcall, 7, 8))
205
        m.dv_rpt1call = ''.join(chr(x)
206
                                for x in warp_byte_size(_mem.rpt1call, 7, 8))
207
        m.dv_rpt2call = ''.join(chr(x)
208
                                for x in warp_byte_size(_mem.rpt2call, 7, 8))
209

    
210
        return m
211

    
212
    def set_memory(self, mem):
213
        num, _mem = self._get_raw_memory(mem.number)
214
        if num < 1000:
215
            _flg = self._memobj.usedflags[num // 8]
216
            _skp = self._memobj.skipflags[num // 8]
217
            _pskp = self._memobj.pskipflags[num // 8]
218
            mybit = 1 << (num % 8)
219
            _flg |= mybit
220
            _skp &= ~mybit
221
            _pskp &= ~mybit
222

    
223
            if mem.empty:
224
                return
225

    
226
            _flg &= ~mybit
227
            if mem.skip == 'S':
228
                _skp |= mybit
229
            elif mem.skip == 'P':
230
                _pskp |= mybit
231

    
232
        if chirp_common.is_6_25(mem.freq):
233
            mult = MULTS[1]
234
        elif chirp_common.is_8_33(mem.freq):
235
            mult = MULTS[2]
236
        else:
237
            mult = MULTS[0]
238
        _mem.mult1 = _mem.mult2 = MULTS.index(mult)
239
        _mem.freq = mem.freq / mult
240
        _mem.offset = mem.offset / mult
241
        _mem.tuning_step = TUNE_STEPS.index(mem.tuning_step)
242
        _mem.name = mem.name.ljust(16)
243
        _mem.mode = MODES.index(mem.mode)
244
        _mem.rtone = chirp_common.TONES.index(mem.rtone)
245
        _mem.ctone = chirp_common.TONES.index(mem.ctone)
246
        _mem.dtcs = chirp_common.DTCS_CODES.index(mem.dtcs)
247
        _mem.dtcs_polarity = DTCS_POL.index(mem.dtcs_polarity)
248
        _mem.tmode = TMODES.index(mem.tmode)
249
        _mem.duplex = DUPLEX.index(mem.duplex)
250

    
251
        _mem.unknown1 = 0
252
        _mem.unknown2 = 0
253
        _mem.unknown3 = 0
254

    
255
        if isinstance(mem, chirp_common.DVMemory):
256
            _mem.dv_code = mem.dv_code
257
            _mem.urcall = list(
258
                warp_byte_size(mem.dv_urcall.ljust(8), 8, 7))
259
            _mem.rpt1call = list(
260
                warp_byte_size(mem.dv_rpt1call.ljust(8), 8, 7))
261
            _mem.rpt2call = list(
262
                warp_byte_size(mem.dv_rpt2call.ljust(8), 8, 7))
(2-2/38)