# HG changeset patch # User Kosta A. ve7kcy@gmail.com # Date 1632769135 25200 # Mon Sep 27 11:58:55 2021 -0700 # Branch ic-v8 # Node ID 3f9fe94cbb74db71f5b091237193d1d2d293469e # Parent 5aa2294d78ea241c6573dcf1929475b072e73685 Initial commit of ICOM IC-V8 driver. Fixes #797
diff --git a/chirp/drivers/icv8.py b/chirp/drivers/icv8.py new file mode 100644 --- /dev/null +++ b/chirp/drivers/icv8.py @@ -0,0 +1,262 @@ +# Copyright 2008 Dan Smith dsmith@danplanet.com +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see http://www.gnu.org/licenses/. + +import logging + +from chirp.drivers import icf +from chirp import chirp_common, memmap, bitwise, errors, directory +from chirp.settings import RadioSetting, RadioSettingGroup, \ + RadioSettingValueInteger, RadioSettingValueList, \ + RadioSettingValueBoolean, RadioSettingValueString, \ + RadioSettingValueFloat, RadioSettings + +LOG = logging.getLogger(__name__) + + +ICV8_MEM_FORMAT = """ + +#seekto 0x0000; +struct { + u16 freq; + u16 offset; + u8 mult:1, + offset_mult:1, + unknown:1, + tmode:2, + unknown:3; + u8 duplex:2, + power:1, + unknown:5; + u8 unknown:6, + reverse_duplex:1, + tx_inhibit:1; + char name[5]; + u8 unknown:2, + rtone:6; + u8 unknown:2, + ctone:6; + u8 unknown:1, + dtcs:7; + u8 unknown; +} memory[107]; + +#seekto 0x06c0; +u8 skip[16]; + +#seekto 0x06d0; +u8 unused[16]; + +""" + +SPECIAL_CHANNELS = { + "1A": 100, "1B": 101, + "2A": 102, "2B": 103, + "3A": 104, "3B": 105, + "C": 106, +} + +TMODES = ["", "Tone", "TSQL", "DTCS"] +SKIPS = ["", "S"] +DUPLEXES = ["", "", "-", "+"] +POWER_LEVELS = [ + chirp_common.PowerLevel("High", watts=5.5), + chirp_common.PowerLevel("Low", watts=0.5) +] + + +@directory.register +class ICV8Radio(icf.IcomCloneModeRadio): + """Icom IC-V8""" + VENDOR = "Icom" + MODEL = "IC-V8" + + _model = "\x24\x68\x00\x01" + _memsize = 1936 + _endframe = "Icom Inc\x2eCD" + + _ranges = [(0x0000, 1936, 16)] + + @classmethod + def get_prompts(cls): + rp = chirp_common.RadioPrompts() + rp.experimental = ("This radio driver is currently under development, " + "and not all the features or functions may work as" + "expected. You should proceed with caution.") + return rp + + def get_features(self): + rf = chirp_common.RadioFeatures() + + rf.memory_bounds = (0, 99) + rf.valid_tmodes = TMODES + rf.valid_duplexes = DUPLEXES + rf.valid_power_levels = POWER_LEVELS + rf.valid_skips = SKIPS + rf.valid_name_length = 5 + rf.valid_special_chans = sorted(SPECIAL_CHANNELS.keys()) + rf.valid_bands = [(136000000, 174000000)] + rf.has_mode = False + rf.has_dtcs_polarity = False + rf.has_tuning_step = False + rf.has_ctone = True + rf.has_offset = True + rf.has_bank = False + rf.has_settings = False + + return rf + + def __init__(self, pipe): + icf.IcomCloneModeRadio.__init__(self, pipe) + + def sync_in(self): + icf.IcomCloneModeRadio.sync_in(self) + + def sync_out(self): + icf.IcomCloneModeRadio.sync_out(self) + + def process_mmap(self): + self._memobj = bitwise.parse(ICV8_MEM_FORMAT, self._mmap) + + def _get_memory(self, number, extd_number = None): + bit = 1 << (number % 8) + byte = int(number / 8) + + _mem = self._memobj.memory[number] + _unused = self._memobj.unused[byte] + _skip = self._memobj.skip[byte] + assert(_mem) + + + mem = chirp_common.Memory(number) + + if not extd_number is None: + mem.extd_number = extd_number + mem.immutable = ["number", "extd_number", "skip"] + if extd_number == "C": + _unused = False + + if (_unused & bit): + mem.empty = True + return mem + + mem.freq = int(_mem.freq) * (_mem.mult and 6250 or 5000) + mem.offset = int(_mem.offset) * (_mem.offset_mult and 6250 or 5000) + mem.name = str(_mem.name).rstrip() + if mem.extd_number == "": + mem.skip = (_skip & bit) and "S" or "" + mem.duplex = DUPLEXES[_mem.duplex] + mem.power = POWER_LEVELS[_mem.power] + mem.tmode = TMODES[_mem.tmode] + mem.rtone = chirp_common.TONES[_mem.rtone] + mem.ctone = chirp_common.TONES[_mem.ctone] + mem.dtcs = chirp_common.DTCS_CODES[_mem.dtcs] + + # Reverse duplex + mem.extra = RadioSettingGroup("extra", "Extra") + rev = RadioSetting("reverse_duplex", "Reverse duplex", + RadioSettingValueBoolean(bool(_mem.reverse_duplex))) + rev.set_doc("Reverse duplex") + mem.extra.append(rev) + + # Tx inhibit + tx_inhibit = RadioSetting("tx_inhibit", "TX inhibit", + RadioSettingValueBoolean(bool(_mem.tx_inhibit))) + tx_inhibit.set_doc("TX inhibit") + mem.extra.append(tx_inhibit) + + return mem + + def get_memory(self, number): + if not self._mmap: + self.sync_in() + + extd_number = None + if isinstance(number, str): + try: + extd_number = number + number = SPECIAL_CHANNELS[number] + except KeyError: + raise errors.InvalidMemoryLocation("Unknown channel %s" % number) + + return self._get_memory(number, extd_number) + + def _fill_memory(self, number): + _mem = self._memobj.memory[number] + assert(_mem) + + # zero-fill + _mem.freq = 146010000 / 6250 + _mem.offset = 600000 / 6250 + _mem.mult = 0x1 + _mem.offset_mult = 0x1 + _mem.name = str("").ljust(5) + _mem.duplex = 0x0 + _mem.reverse_duplex = 0x0 + _mem.tx_inhibit = 0x0 + _mem.power = 0x0 + _mem.tmode = 0x0 + _mem.rtone = 0x8 + _mem.ctone = 0x8 + _mem.dtcs = 0x0 + + for setting in mem.extra: + setattr(_mem, setting.get_name(), setting.value) + + + def _set_memory(self, mem): + bit = 1 << (number % 8) + byte = int(number / 8) + + _mem = self._memobj.memory[mem.number] + _unused = self._memobj.unused[byte] + _skip = (mem.extd_number == "") and self._memobj.skip[byte] else None + assert(_mem) + + if mem.empty: + self._fill_memory(mem.number) + _unused |= bit + if skip is not None: + _skip |= bit + return + + _mem.mult = chirp_common.is_fractional_step(mem.freq) + _mem.freq = mem.freq / (_mem.mult and 6250 or 5000) + _mem.offset_mult = chirp_common.is_fractional_step(mem.offset) + _mem.offset = int(mem.offset) / (_mem.offset_mult and 6250 or 5000) + _mem.name = str(mem.name).ljust(5) + _mem.duplex = DUPLEXES.index(mem.duplex) + _mem.power = POWER_LEVELS.index(mem.power) + _mem.tmode = TMODES.index(mem.tmode) + _mem.rtone = chirp_common.TONES.index(mem.rtone) + _mem.ctone = chirp_common.TONES.index(mem.ctone) + _mem.dtcs = chirp_common.DTCS_CODES.index(mem.dtcs) + + # Set used + _unused &= ~bit + + # Set skip + if _skip is not None: + if mem.skip == "S": _skip |= bit + else: _skip &= ~bit + + def set_memory(self, mem): + if not self._mmap: + self.sync_in() + assert(self._mmap) + + return self._set_memory(mem) + + def get_raw_memory(self, number): + return repr(self._memobj.memory[number])