Project

General

Profile

Bug #10826 » id800.py

818f831f - Dan Smith, 09/05/2023 05:11 PM

 
1
# Copyright 2008 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.drivers import icf
17
from chirp import chirp_common, errors, directory, bitwise
18

    
19
MEM_FORMAT = """
20
#seekto 0x0020;
21
struct {
22
  u24 freq;
23
  u16 offset;
24
  u8  power:2,
25
      rtone:6;
26
  u8  duplex:2,
27
      ctone:6;
28
  u8  dtcs;
29
  u8  tuning_step:4,
30
      unknown1:4;
31
  u8  unknown2;
32
  u8  mult_flag:1,
33
      unknown3:5,
34
      tmode:2;
35
  u16 dtcs_polarity:2,
36
      usealpha:1,
37
      empty:1,
38
      name1:6,
39
      name2:6;
40
  u24 name3:6,
41
      name4:6,
42
      name5:6,
43
      name6:6;
44
  u8 unknown5;
45
  u8 unknown6:1,
46
     digital_code:7;
47
  u8 urcall;
48
  u8 rpt1call;
49
  u8 rpt2call;
50
  u8 unknown7:1,
51
     mode:3,
52
     unknown8:4;
53
} memory[512];
54

    
55
#seekto 0x2BF4;
56
struct {
57
  u8 unknown1:1,
58
     empty:1,
59
     pskip:1,
60
     skip:1,
61
     bank:4;
62
} flags[512];
63

    
64
#seekto 0x3220;
65
struct {
66
  char call[8];
67
} mycalls[8];
68

    
69
#seekto 0x3250;
70
struct {
71
  char call[8];
72
} urcalls[99];
73

    
74
#seekto 0x3570;
75
struct {
76
  char call[8];
77
} rptcalls[59];
78
"""
79

    
80
MODES = ["FM", "NFM", "AM", "NAM", "DV", "FM", "FM", "FM"]
81
TMODES = ["", "Tone", "TSQL", "DTCS"]
82
DUPLEX = ["", "", "-", "+"]
83
DTCS_POL = ["NN", "NR", "RN", "RR"]
84
STEPS = [5.0, 10.0, 12.5, 15, 20.0, 25.0, 30.0, 50.0, 100.0, 200.0, 6.25]
85
POWER_LEVELS = [chirp_common.PowerLevel('High', watts=50),
86
                chirp_common.PowerLevel('Low', watts=5),
87
                chirp_common.PowerLevel('Mid', watts=15)]
88

    
89
ID800_SPECIAL = {
90
    "C2": 510,
91
    "C1": 511,
92
    }
93
ID800_SPECIAL_REV = {
94
    510: "C2",
95
    511: "C1",
96
    }
97

    
98
for i in range(0, 5):
99
    idA = "%iA" % (i + 1)
100
    idB = "%iB" % (i + 1)
101
    num = 500 + i * 2
102
    ID800_SPECIAL[idA] = num
103
    ID800_SPECIAL[idB] = num + 1
104
    ID800_SPECIAL_REV[num] = idA
105
    ID800_SPECIAL_REV[num+1] = idB
106

    
107
ALPHA_CHARSET = " ABCDEFGHIJKLMNOPQRSTUVWXYZ"
108
NUMERIC_CHARSET = "0123456789+-=*/()|"
109

    
110

    
111
def get_name(_mem):
112
    """Decode the name from @_mem"""
113
    def _get_char(val):
114
        if val == 0:
115
            return " "
116
        elif val & 0x20:
117
            return ALPHA_CHARSET[val & 0x1F]
118
        else:
119
            return NUMERIC_CHARSET[val & 0x0F]
120

    
121
    name_bytes = [_mem.name1, _mem.name2, _mem.name3,
122
                  _mem.name4, _mem.name5, _mem.name6]
123
    name = ""
124
    for val in name_bytes:
125
        name += _get_char(val)
126

    
127
    return name.rstrip()
128

    
129

    
130
def set_name(_mem, name):
131
    """Encode @name in @_mem"""
132
    def _get_index(char):
133
        if char == " ":
134
            return 0
135
        elif char.isalpha():
136
            return ALPHA_CHARSET.index(char) | 0x20
137
        else:
138
            return NUMERIC_CHARSET.index(char) | 0x10
139

    
140
    name = name.ljust(6)[:6]
141

    
142
    _mem.usealpha = bool(name.strip())
143

    
144
    # The element override calling convention makes this harder to automate.
145
    # It's just six, so do it manually
146
    _mem.name1 = _get_index(name[0])
147
    _mem.name2 = _get_index(name[1])
148
    _mem.name3 = _get_index(name[2])
149
    _mem.name4 = _get_index(name[3])
150
    _mem.name5 = _get_index(name[4])
151
    _mem.name6 = _get_index(name[5])
152

    
153

    
154
@directory.register
155
class ID800v2Radio(icf.IcomCloneModeRadio, chirp_common.IcomDstarSupport):
156
    """Icom ID800"""
157
    VENDOR = "Icom"
158
    MODEL = "ID-800H"
159
    VARIANT = "v2"
160

    
161
    _model = "\x27\x88\x02\x00"
162
    _memsize = 14528
163
    _endframe = "Icom Inc\x2eCB"
164
    _can_hispeed = True
165

    
166
    _memories = []
167

    
168
    _ranges = [(0x0020, 0x2B18, 32),
169
               (0x2B18, 0x2B20,  8),
170
               (0x2B20, 0x2BE0, 32),
171
               (0x2BE0, 0x2BF4, 20),
172
               (0x2BF4, 0x2C00, 12),
173
               (0x2C00, 0x2DE0, 32),
174
               (0x2DE0, 0x2DF4, 20),
175
               (0x2DF4, 0x2E00, 12),
176
               (0x2E00, 0x2E20, 32),
177

    
178
               (0x2F00, 0x3070, 32),
179

    
180
               (0x30D0, 0x30E0, 16),
181
               (0x30E0, 0x3160, 32),
182
               (0x3160, 0x3180, 16),
183
               (0x3180, 0x31A0, 32),
184
               (0x31A0, 0x31B0, 16),
185

    
186
               (0x3220, 0x3240, 32),
187
               (0x3240, 0x3260, 16),
188
               (0x3260, 0x3560, 32),
189
               (0x3560, 0x3580, 16),
190
               (0x3580, 0x3720, 32),
191
               (0x3720, 0x3780,  8),
192

    
193
               (0x3798, 0x37A0,  8),
194
               (0x37A0, 0x37B0, 16),
195
               (0x37B0, 0x37B1,  1),
196

    
197
               (0x37D8, 0x37E0,  8),
198
               (0x37E0, 0x3898, 32),
199
               (0x3898, 0x389A,  2),
200

    
201
               (0x38A8, 0x38C0, 16), ]
202

    
203
    MYCALL_LIMIT = (1, 7)
204
    URCALL_LIMIT = (1, 99)
205
    RPTCALL_LIMIT = (1, 59)
206

    
207
    def _get_bank(self, loc):
208
        _flg = self._memobj.flags[loc-1]
209
        if _flg.bank >= 0x0A:
210
            return None
211
        else:
212
            return _flg.bank
213

    
214
    def _set_bank(self, loc, bank):
215
        _flg = self._memobj.flags[loc-1]
216
        if bank is None:
217
            _flg.bank = 0x0A
218
        else:
219
            _flg.bank = bank
220

    
221
    def get_features(self):
222
        rf = chirp_common.RadioFeatures()
223
        rf.has_implicit_calls = True
224
        rf.has_settings = True
225
        rf.has_bank = True
226
        rf.valid_modes = [x for x in MODES if x]
227
        rf.valid_tmodes = ["", "Tone", "TSQL", "DTCS"]
228
        rf.valid_duplexes = ["", "-", "+"]
229
        rf.valid_tuning_steps = list(STEPS)
230
        rf.valid_bands = [(118000000, 173995000), (230000000, 549995000),
231
                          (810000000, 999990000)]
232
        rf.valid_skips = ["", "S", "P"]
233
        rf.valid_name_length = 6
234
        rf.valid_special_chans = sorted(ID800_SPECIAL.keys())
235
        rf.valid_power_levels = list(POWER_LEVELS)
236
        rf.memory_bounds = (1, 499)
237
        return rf
238

    
239
    def process_mmap(self):
240
        self._memobj = bitwise.parse(MEM_FORMAT, self._mmap)
241

    
242
    def get_memory(self, number):
243
        if isinstance(number, str):
244
            try:
245
                number = ID800_SPECIAL[number] + 1  # Because we subtract below
246
            except KeyError:
247
                raise errors.InvalidMemoryLocation("Unknown channel %s" %
248
                                                   number)
249

    
250
        _mem = self._memobj.memory[number-1]
251
        _flg = self._memobj.flags[number-1]
252

    
253
        if MODES[_mem.mode] == "DV":
254
            urcalls = self.get_urcall_list()
255
            rptcalls = self.get_repeater_call_list()
256
            mem = chirp_common.DVMemory()
257
            mem.dv_urcall = urcalls[_mem.urcall]
258
            mem.dv_rpt1call = rptcalls[_mem.rpt1call]
259
            mem.dv_rpt2call = rptcalls[_mem.rpt2call]
260
            mem.dv_code = _mem.digital_code
261
        else:
262
            mem = chirp_common.Memory()
263

    
264
        mem.number = number
265
        if _flg.empty:
266
            mem.empty = True
267
            return mem
268

    
269
        mult = _mem.mult_flag and 6250 or 5000
270
        mem.freq = _mem.freq * mult
271
        mem.offset = _mem.offset * 5000
272
        mem.duplex = DUPLEX[_mem.duplex]
273
        mem.mode = MODES[_mem.mode]
274
        mem.tmode = TMODES[_mem.tmode]
275
        mem.rtone = chirp_common.TONES[_mem.rtone]
276
        mem.ctone = chirp_common.TONES[_mem.ctone]
277
        mem.dtcs = chirp_common.DTCS_CODES[_mem.dtcs]
278
        mem.dtcs_polarity = DTCS_POL[_mem.dtcs_polarity]
279
        mem.tuning_step = STEPS[_mem.tuning_step]
280
        mem.name = get_name(_mem)
281
        mem.power = POWER_LEVELS[_mem.power]
282

    
283
        mem.skip = _flg.pskip and "P" or _flg.skip and "S" or ""
284

    
285
        return mem
286

    
287
    def set_memory(self, mem):
288
        _mem = self._memobj.memory[mem.number-1]
289
        _flg = self._memobj.flags[mem.number-1]
290

    
291
        _flg.empty = mem.empty
292
        if mem.empty:
293
            self._set_bank(mem.number, None)
294
            return
295

    
296
        mult = chirp_common.is_fractional_step(mem.freq) and 6250 or 5000
297
        _mem.mult_flag = mult == 6250
298
        _mem.freq = mem.freq / mult
299
        _mem.offset = mem.offset / 5000
300
        _mem.duplex = DUPLEX.index(mem.duplex)
301
        _mem.mode = MODES.index(mem.mode)
302
        _mem.tmode = TMODES.index(mem.tmode)
303
        _mem.rtone = chirp_common.TONES.index(mem.rtone)
304
        _mem.ctone = chirp_common.TONES.index(mem.ctone)
305
        _mem.dtcs = chirp_common.DTCS_CODES.index(mem.dtcs)
306
        _mem.dtcs_polarity = DTCS_POL.index(mem.dtcs_polarity)
307
        _mem.tuning_step = STEPS.index(mem.tuning_step)
308
        if mem.power in POWER_LEVELS:
309
            _mem.power = POWER_LEVELS.index(mem.power)
310
        set_name(_mem, mem.name)
311

    
312
        _flg.pskip = mem.skip == "P"
313
        _flg.skip = mem.skip == "S"
314

    
315
        if mem.mode == "DV":
316
            urcalls = self.get_urcall_list()
317
            rptcalls = self.get_repeater_call_list()
318
            if not isinstance(mem, chirp_common.DVMemory):
319
                raise errors.InvalidDataError("DV mode is not a DVMemory!")
320
            try:
321
                err = mem.dv_urcall
322
                _mem.urcall = urcalls.index(mem.dv_urcall)
323
                err = mem.dv_rpt1call
324
                _mem.rpt1call = rptcalls.index(mem.dv_rpt1call)
325
                err = mem.dv_rpt2call
326
                _mem.rpt2call = rptcalls.index(mem.dv_rpt2call)
327
            except IndexError:
328
                raise errors.InvalidDataError("DV Call %s not in list" % err)
329
        else:
330
            _mem.urcall = 0
331
            _mem.rpt1call = 0
332
            _mem.rpt2call = 0
333

    
334
    def sync_in(self):
335
        icf.IcomCloneModeRadio.sync_in(self)
336
        self.process_mmap()
337

    
338
    def get_raw_memory(self, number):
339
        return repr(self._memobj.memory[number-1])
340

    
341
    def get_urcall_list(self):
342
        calls = ["CQCQCQ"]
343

    
344
        for i in range(*self.URCALL_LIMIT):
345
            calls.append(str(self._memobj.urcalls[i-1].call).rstrip())
346

    
347
        return calls
348

    
349
    def get_repeater_call_list(self):
350
        calls = ["*NOTUSE*"]
351

    
352
        for i in range(*self.RPTCALL_LIMIT):
353
            calls.append(str(self._memobj.rptcalls[i-1].call).rstrip())
354

    
355
        return calls
356

    
357
    def get_mycall_list(self):
358
        calls = []
359

    
360
        for i in range(*self.MYCALL_LIMIT):
361
            calls.append(str(self._memobj.mycalls[i-1].call).rstrip())
362

    
363
        return calls
364

    
365
    def set_urcall_list(self, calls):
366
        for i in range(*self.URCALL_LIMIT):
367
            try:
368
                call = calls[i].upper()  # Skip the implicit CQCQCQ
369
            except IndexError:
370
                call = " " * 8
371

    
372
            self._memobj.urcalls[i-1].call = call.ljust(8)[:8]
373

    
374
    def set_repeater_call_list(self, calls):
375
        for i in range(*self.RPTCALL_LIMIT):
376
            try:
377
                call = calls[i].upper()  # Skip the implicit blank
378
            except IndexError:
379
                call = " " * 8
380

    
381
            self._memobj.rptcalls[i-1].call = call.ljust(8)[:8]
382

    
383
    def set_mycall_list(self, calls):
384
        for i in range(*self.MYCALL_LIMIT):
385
            try:
386
                call = calls[i-1].upper()
387
            except IndexError:
388
                call = " " * 8
389

    
390
            self._memobj.mycalls[i-1].call = call.ljust(8)[:8]
(10-10/10)