Project

General

Profile

Bug #6617 » ic2300_20200203.py

Dan Smith, 02/03/2020 04:00 PM

 
1
# Copyright 2017 Windsor Schmidt <windsor.schmidt@gmail.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, directory, bitwise
17
from chirp.drivers import icf
18
from chirp.settings import RadioSetting, RadioSettingGroup, \
19
    RadioSettingValueList, RadioSettingValueBoolean, RadioSettings
20

    
21
# The Icom IC-2300H is a 65W, 144MHz mobile transceiver based on the IC-2200H.
22
# Unlike the IC-2200H, this model does not accept Icom's UT-118 D-STAR board.
23
#
24
# A simple USB interface based on a typical FT232RL breakout board was used
25
# during development of this module. A schematic diagram is as follows:
26
#
27
#
28
# 3.5mm plug from IC-2300H
29
# sleeve / ring / tip
30
# --.______________
31
#   |      |   |   \
32
#   |______|___|___/    FT232RL breakout
33
# --'    |   |        .------------------.
34
#        |   +--------| RXD              |
35
#        |   |   D1   |                  |
36
#        |   +--|>|---| TXD              | USB/PC
37
#        |   |   R1   |                  |-------->
38
#        |   +--[_]---| VCC (5V)         |
39
#        |            |                  |
40
#        +------------| GND              |
41
#                     `------------------'
42
#
43
# D1: 1N4148 shottky diode
44
# R1: 10K ohm resistor
45

    
46
MEM_FORMAT = """
47
#seekto 0x0000; // channel memories
48
struct {
49
  ul16 frequency;
50
  ul16 offset;
51
  char name[6];
52
  u8   repeater_tone;
53
  u8   ctcss_tone;
54
  u8   dtcs_code;
55
  u8   tuning_step:4,
56
       tone_mode:4;
57
  u8   unknown1:3,
58
       mode_narrow:1,
59
       power:2,
60
       unknown2:2;
61
  u8   dtcs_polarity:2,
62
       duplex:2,
63
       unknown3:1,
64
       reverse_duplex:1,
65
       unknown4:1,
66
       display_style:1;
67
} memory[200];
68
#seekto 0x1340; // channel memory flags
69
struct {
70
  u8   unknown5:2,
71
       empty:1,
72
       skip:1,
73
       bank:4;
74
} flags[200];
75
#seekto 0x1660; // power-on and regular set menu items
76
struct {
77
  u8   key_beep;
78
  u8   tx_timeout;
79
  u8   auto_repeater;
80
  u8   auto_power_off;
81
  u8   repeater_lockout;
82
  u8   squelch_delay;
83
  u8   squelch_type;
84
  u8   dtmf_speed;
85
  u8   display_type;
86
  u8   unknown6;
87
  u8   tone_burst;
88
  u8   voltage_display;
89
  u8   unknown7;
90
  u8   display_brightness;
91
  u8   display_color;
92
  u8   auto_dimmer;
93
  u8   display_contrast;
94
  u8   scan_pause_timer;
95
  u8   mic_gain;
96
  u8   scan_resume_timer;
97
  u8   weather_alert;
98
  u8   bank_link_enable;
99
  u8   bank_link[10];
100
} settings;
101
"""
102

    
103
TUNING_STEPS = [5.0, 6.25, 10.0, 12.5, 15.0, 20.0, 25.0, 30.0, 50.0]
104
TONE_MODES = ["", "Tone", "TSQL", "DTCS"]
105
DUPLEX = ["", "-", "+"]
106
DTCSP = ["NN", "NR", "RN", "RR"]
107
DTCS_POLARITY = ["NN", "NR", "RN", "RR"]
108

    
109
POWER_LEVELS = [chirp_common.PowerLevel("High", watts=65),
110
                chirp_common.PowerLevel("Low", watts=5),
111
                chirp_common.PowerLevel("MidLow", watts=10),
112
                chirp_common.PowerLevel("Mid", watts=25)]
113

    
114

    
115
def _wipe_memory(mem, char):
116
    mem.set_raw(char * (mem.size() / 8))
117

    
118

    
119
@directory.register
120
class IC2300Radio(icf.IcomCloneModeRadio):
121
    """Icom IC-2300"""
122
    VENDOR = "Icom"
123
    MODEL = "IC-2300H"
124

    
125
    _model = "\x32\x51\x00\x01"
126
    _memsize = 6304
127
    _endframe = "Icom Inc.C5\xfd"
128
    _can_hispeed = True
129
    _ranges = [(0x0000, 0x18a0, 32)]  # upload entire memory for now
130

    
131
    def get_features(self):
132
        rf = chirp_common.RadioFeatures()
133
        rf.memory_bounds = (0, 199)
134
        rf.valid_modes = ["FM", "NFM"]
135
        rf.valid_tmodes = list(TONE_MODES)
136
        rf.valid_duplexes = list(DUPLEX)
137
        rf.valid_tuning_steps = list(TUNING_STEPS)
138
        rf.valid_bands = [(136000000, 174000000)]  # USA tx range: 144-148MHz
139
        rf.valid_skips = ["", "S"]
140
        rf.valid_power_levels = POWER_LEVELS
141
        rf.has_settings = True
142
        return rf
143

    
144
    def process_mmap(self):
145
        self._memobj = bitwise.parse(MEM_FORMAT, self._mmap)
146

    
147
    def _get_bank(self, loc):
148
        _flag = self._memobj.flags[loc]
149
        if _flag.bank == 0x0a:
150
            return None
151
        else:
152
            return _flag.bank
153

    
154
    def _set_bank(self, loc, bank):
155
        _flag = self._memobj.flags[loc]
156
        if bank is None:
157
            _flag.bank = 0x0a
158
        else:
159
            _flag.bank = bank
160

    
161
    def get_memory(self, number):
162
        _mem = self._memobj.memory[number]
163
        _flag = self._memobj.flags[number]
164
        mem = chirp_common.Memory()
165
        mem.number = number
166
        if _flag.empty:
167
            mem.empty = True
168
            return mem
169
        mult = int(TUNING_STEPS[_mem.tuning_step] * 1000)
170
        mem.freq = (_mem.frequency * mult)
171
        mem.offset = (_mem.offset * mult)
172
        mem.name = str(_mem.name).rstrip()
173
        mem.rtone = chirp_common.TONES[_mem.repeater_tone]
174
        mem.ctone = chirp_common.TONES[_mem.ctcss_tone]
175
        mem.dtcs = chirp_common.DTCS_CODES[_mem.dtcs_code]
176
        mem.tuning_step = TUNING_STEPS[_mem.tuning_step]
177
        mem.tmode = TONE_MODES[_mem.tone_mode]
178
        mem.mode = "NFM" if _mem.mode_narrow else "FM"
179
        mem.dtcs_polarity = DTCS_POLARITY[_mem.dtcs_polarity]
180
        mem.duplex = DUPLEX[_mem.duplex]
181
        mem.skip = "S" if _flag.skip else ""
182
        mem.power = POWER_LEVELS[_mem.power]
183

    
184
        # Reverse duplex
185
        mem.extra = RadioSettingGroup("extra", "Extra")
186
        rev = RadioSetting("reverse_duplex", "Reverse duplex",
187
                           RadioSettingValueBoolean(bool(_mem.reverse_duplex)))
188
        rev.set_doc("Reverse duplex")
189
        mem.extra.append(rev)
190

    
191
        # Memory display style
192
        opt = ["Frequency", "Label"]
193
        dsp = RadioSetting("display_style", "Display style",
194
                           RadioSettingValueList(opt, opt[_mem.display_style]))
195
        dsp.set_doc("Memory display style")
196
        mem.extra.append(dsp)
197

    
198
        return mem
199

    
200
    def get_raw_memory(self, number):
201
        return repr(self._memobj.memory[number])
202

    
203
    def set_memory(self, mem):
204
        number = mem.number
205
        _mem = self._memobj.memory[number]
206
        _flag = self._memobj.flags[number]
207
        was_empty = int(_flag.empty)
208
        _flag.empty = mem.empty
209
        if mem.empty:
210
            _wipe_memory(_mem, "\xff")
211
            return
212
        if was_empty:
213
            _wipe_memory(_mem, "\x00")
214
        mult = mem.tuning_step * 1000
215
        _mem.frequency = (mem.freq / mult)
216
        _mem.offset = mem.offset / mult
217
        _mem.name = mem.name.ljust(6)
218
        _mem.repeater_tone = chirp_common.TONES.index(mem.rtone)
219
        _mem.ctcss_tone = chirp_common.TONES.index(mem.ctone)
220
        _mem.dtcs_code = chirp_common.DTCS_CODES.index(mem.dtcs)
221
        _mem.tuning_step = TUNING_STEPS.index(mem.tuning_step)
222
        _mem.tone_mode = TONE_MODES.index(mem.tmode)
223
        _mem.mode_narrow = mem.mode.startswith("N")
224
        _mem.dtcs_polarity = DTCSP.index(mem.dtcs_polarity)
225
        _mem.duplex = DUPLEX.index(mem.duplex)
226
        if mem.power:
227
            _mem.power = POWER_LEVELS.index(mem.power)
228
        else:
229
            _mem.power = POWER_LEVELS[0]
230
        _flag.skip = mem.skip != ""
231

    
232
        for setting in mem.extra:
233
            setattr(_mem, setting.get_name(), setting.value)
234

    
235
    def get_settings(self):
236
        _settings = self._memobj.settings
237
        basic = RadioSettingGroup("basic", "Basic Settings")
238
        front_panel = RadioSettingGroup("front_panel", "Front Panel Settings")
239
        top = RadioSettings(basic, front_panel)
240

    
241
        # Transmit timeout
242
        opt = ['Disabled', '1 minute'] + \
243
              [s + ' minutes' for s in map(str, range(2, 31))]
244
        rs = RadioSetting("tx_timeout", "Transmit timeout (min)",
245
                          RadioSettingValueList(opt, opt[
246
                              _settings.tx_timeout
247
                          ]))
248
        basic.append(rs)
249

    
250
        # Auto Repeater (USA model only)
251
        opt = ["Disabled", "Duplex Only", "Duplex and tone"]
252
        rs = RadioSetting("auto_repeater", "Auto repeater",
253
                          RadioSettingValueList(opt, opt[
254
                              _settings.auto_repeater
255
                          ]))
256
        basic.append(rs)
257

    
258
        # Auto Power Off
259
        opt = ["Disabled", "30 minutes", "60 minutes", "120 minutes"]
260
        rs = RadioSetting("auto_power_off", "Auto power off",
261
                          RadioSettingValueList(opt, opt[
262
                              _settings.auto_power_off
263
                          ]))
264
        basic.append(rs)
265

    
266
        # Squelch Delay
267
        opt = ["Short", "Long"]
268
        rs = RadioSetting("squelch_delay", "Squelch delay",
269
                          RadioSettingValueList(opt, opt[
270
                              _settings.squelch_delay
271
                          ]))
272
        basic.append(rs)
273

    
274
        # Squelch Type
275
        opt = ["Noise squelch", "S-meter squelch", "Squelch attenuator"]
276
        rs = RadioSetting("squelch_type", "Squelch type",
277
                          RadioSettingValueList(opt, opt[
278
                              _settings.squelch_type
279
                          ]))
280
        basic.append(rs)
281

    
282
        # Repeater Lockout
283
        opt = ["Disabled", "Repeater lockout", "Busy lockout"]
284
        rs = RadioSetting("repeater_lockout", "Repeater lockout",
285
                          RadioSettingValueList(opt, opt[
286
                              _settings.repeater_lockout
287
                          ]))
288
        basic.append(rs)
289

    
290
        # DTMF Speed
291
        opt = ["100ms interval, 5.0 cps",
292
               "200ms interval, 2.5 cps",
293
               "300ms interval, 1.6 cps",
294
               "500ms interval, 1.0 cps"]
295
        rs = RadioSetting("dtmf_speed", "DTMF speed",
296
                          RadioSettingValueList(opt, opt[
297
                              _settings.dtmf_speed
298
                          ]))
299
        basic.append(rs)
300

    
301
        # Scan pause timer
302
        opt = [s + ' seconds' for s in map(str, range(2, 22, 2))] + ['Hold']
303
        rs = RadioSetting("scan_pause_timer", "Scan pause timer",
304
                          RadioSettingValueList(
305
                              opt, opt[_settings.scan_pause_timer]))
306
        basic.append(rs)
307

    
308
        # Scan Resume Timer
309
        opt = ['Immediate'] + \
310
              [s + ' seconds' for s in map(str, range(1, 6))] + ['Hold']
311
        rs = RadioSetting("scan_resume_timer", "Scan resume timer",
312
                          RadioSettingValueList(
313
                              opt, opt[_settings.scan_resume_timer]))
314
        basic.append(rs)
315

    
316
        # Weather Alert (USA model only)
317
        rs = RadioSetting("weather_alert", "Weather alert",
318
                          RadioSettingValueBoolean(_settings.weather_alert))
319
        basic.append(rs)
320

    
321
        # Tone Burst
322
        rs = RadioSetting("tone_burst", "Tone burst",
323
                          RadioSettingValueBoolean(_settings.tone_burst))
324
        basic.append(rs)
325

    
326
        # Memory Display Type
327
        opt = ["Frequency", "Channel", "Name"]
328
        rs = RadioSetting("display_type", "Memory display",
329
                          RadioSettingValueList(opt,
330
                                                opt[_settings.display_type]))
331
        front_panel.append(rs)
332

    
333
        # Display backlight brightness;
334
        opt = ["1 (dimmest)", "2", "3", "4 (brightest)"]
335
        rs = RadioSetting("display_brightness", "Backlight brightness",
336
                          RadioSettingValueList(
337
                              opt,
338
                              opt[_settings.display_brightness]))
339
        front_panel.append(rs)
340

    
341
        # Display backlight color
342
        opt = ["Amber", "Yellow", "Green"]
343
        rs = RadioSetting("display_color", "Backlight color",
344
                          RadioSettingValueList(opt,
345
                                                opt[_settings.display_color]))
346
        front_panel.append(rs)
347

    
348
        # Display contrast
349
        opt = ["1 (lightest)", "2", "3", "4 (darkest)"]
350
        rs = RadioSetting("display_contrast", "Display contrast",
351
                          RadioSettingValueList(
352
                              opt,
353
                              opt[_settings.display_contrast]))
354
        front_panel.append(rs)
355

    
356
        # Auto dimmer
357
        opt = ["Disabled", "Backlight off", "1 (dimmest)", "2", "3"]
358
        rs = RadioSetting("auto_dimmer", "Auto dimmer",
359
                          RadioSettingValueList(opt,
360
                                                opt[_settings.auto_dimmer]))
361
        front_panel.append(rs)
362

    
363
        # Microphone gain
364
        opt = ["Low", "High"]
365
        rs = RadioSetting("mic_gain", "Microphone gain",
366
                          RadioSettingValueList(opt,
367
                                                opt[_settings.mic_gain]))
368
        front_panel.append(rs)
369

    
370
        # Key press beep
371
        rs = RadioSetting("key_beep", "Key press beep",
372
                          RadioSettingValueBoolean(_settings.key_beep))
373
        front_panel.append(rs)
374

    
375
        # Voltage Display;
376
        rs = RadioSetting("voltage_display", "Voltage display",
377
                          RadioSettingValueBoolean(_settings.voltage_display))
378
        front_panel.append(rs)
379

    
380
        # TODO: Add Bank Links settings to GUI
381

    
382
        return top
383

    
384
    def set_settings(self, settings):
385
        _settings = self._memobj.settings
386
        for element in settings:
387
            if not isinstance(element, RadioSetting):
388
                self.set_settings(element)
389
                continue
390
            setting = element.get_name()
391
            setattr(_settings, setting, element.value)
(6-6/7)