# HG changeset patch # User Iain White # Date 1593451252 14400 # Mon Jun 29 13:20:52 2020 -0400 # Node ID 737bdbd77e47293ce4642f42dc8ff7741577fe78 # Parent 0c5db792de719d4f1c373df76bcf74a51e87fc0b Add new driver for the Yaesu FTM-7250D radio based on the FTM3200 driver diff --git a/chirp/drivers/ftm7250d.py b/chirp/drivers/ftm7250d.py new file mode 100644 --- /dev/null +++ b/chirp/drivers/ftm7250d.py @@ -0,0 +1,202 @@ +# Copyright 2010 Dan Smith +# Copyright 2017 Wade Simmons +# FTM-7250D driver cloned from the FTM-3200 and modified by Iain White +# +# 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 . + +import logging +from textwrap import dedent + +from chirp.drivers import yaesu_clone, ft1d +from chirp import chirp_common, directory, bitwise +from chirp.settings import RadioSettings + +LOG = logging.getLogger(__name__) + +POWER_LEVELS = [chirp_common.PowerLevel("Low", watts=5), + chirp_common.PowerLevel("Mid", watts=25), + chirp_common.PowerLevel("Hi", watts=50)] + +TMODES = ["", "Tone", "TSQL", "DTCS", "TSQL-R", None, None, "Pager", "Cross"] +CROSS_MODES = [None, "DTCS->", "Tone->DTCS", "DTCS->Tone"] + +MODES = ["FM", "NFM"] +STEPS = [0, 5, 6.25, 10, 12.5, 15, 20, 25, 50, 100] # 0 = auto +RFSQUELCH = ["OFF", "S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8"] + +# Charset is subset of ASCII + some unknown chars \x80-\x86 +VALID_CHARS = ["%i" % int(x) for x in range(0, 10)] + \ + list(":>=After clicking OK, press the [GM(AMS)] key + to send image.""")) + rp.pre_upload = _(dedent("""\ + 1. Turn radio off. + 2. Connect cable to MIC Jack. + 3. Press and hold in the [MHz(SETUP)] key while turning the radio + on ("CLONE" will appear on the display). + 4. Press the [MHz(SETUP)] key + ("-WAIT-" will appear on the LCD).""")) + return rp + + def process_mmap(self): + mem_format = ft1d.MEM_FORMAT + MEM_FORMAT + self._memobj = bitwise.parse(mem_format % self._mem_params, self._mmap) + + def get_features(self): + rf = chirp_common.RadioFeatures() + rf.has_dtcs_polarity = False + rf.valid_modes = list(MODES) + rf.valid_tmodes = [x for x in TMODES if x is not None] + rf.valid_cross_modes = [x for x in CROSS_MODES if x is not None] + rf.valid_duplexes = list(ft1d.DUPLEX) + rf.valid_tuning_steps = list(STEPS) + rf.valid_bands = [(136000000, 174000000),(420000000, 450000000)] + # rf.valid_skips = SKIPS + rf.valid_power_levels = POWER_LEVELS + rf.valid_characters = "".join(VALID_CHARS) + rf.valid_name_length = 8 + rf.memory_bounds = (1, 199) + rf.can_odd_split = True + rf.has_ctone = False + rf.has_bank = False + rf.has_bank_names = False + # disable until implemented + rf.has_settings = False + return rf + + def _decode_label(self, mem): + # TODO preserve the unknown \x80-x86 chars? + return str(mem.label).rstrip("\xFF").decode('ascii', 'replace') + + def _encode_label(self, mem): + label = mem.name.rstrip().encode('ascii', 'ignore') + return self._add_ff_pad(label, 16) + + def _encode_charsetbits(self, mem): + # TODO this is a setting to decide if the memory should be displayed + # as a name or frequency. Should we expose this setting to the user + # instead of autoselecting it (and losing their preference)? + if mem.name.rstrip() == '': + return [0x00, 0x00] + return [0x00, 0x80] + + def _decode_power_level(self, mem): + return POWER_LEVELS[mem.power - 1] + + def _encode_power_level(self, mem): + return POWER_LEVELS.index(mem.power) + 1 + + def _decode_mode(self, mem): + return MODES[mem.mode_alt] + + def _encode_mode(self, mem): + return MODES.index(mem.mode) + + def _get_tmode(self, mem, _mem): + if _mem.tone_mode > 8: + tmode = "Cross" + mem.cross_mode = CROSS_MODES[_mem.tone_mode - 8] + else: + tmode = TMODES[_mem.tone_mode] + + if tmode == "Pager": + # TODO chirp_common does not allow 'Pager' + # Expose as a different setting? + mem.tmode = "" + else: + mem.tmode = tmode + + def _set_tmode(self, _mem, mem): + if mem.tmode == "Cross": + _mem.tone_mode = 8 + CROSS_MODES.index(mem.cross_mode) + else: + _mem.tone_mode = TMODES.index(mem.tmode) + + def _set_mode(self, _mem, mem): + _mem.mode_alt = self._encode_mode(mem) + + def get_bank_model(self): + return None + + def _debank(self, mem): + return + + def _checksums(self): + return [yaesu_clone.YaesuChecksum(0x064A, 0x06C8), + yaesu_clone.YaesuChecksum(0x06CA, 0x0748), + yaesu_clone.YaesuChecksum(0x074A, 0x07C8), + yaesu_clone.YaesuChecksum(0x07CA, 0x0848), + yaesu_clone.YaesuChecksum(0x0000, 0xFEC9)] + + def _get_settings(self): + # TODO + top = RadioSettings() + return top + + @classmethod + def _wipe_memory(cls, mem): + mem.set_raw("\x00" * (mem.size() / 8)) + + def sync_out(self): + # Need to give enough time for the radio to ACK after writes + self.pipe.timeout = 1 + return super(FTM7250Radio, self).sync_out()