Project

General

Profile

Bug #10251 » ftm7250d.py

UNSAFE FOR UPLOAD test driver - Dan Smith, 01/12/2023 12:03 AM

 
1
# Copyright 2010 Dan Smith <dsmith@danplanet.com>
2
# Copyright 2017 Wade Simmons <wade@wades.im>
3
# FTM-7250D driver cloned from the FTM-3200 and modified by Iain White
4
#
5
# This program is free software: you can redistribute it and/or modify
6
# it under the terms of the GNU General Public License as published by
7
# the Free Software Foundation, either version 3 of the License, or
8
# (at your option) any later version.
9
#
10
# This program is distributed in the hope that it will be useful,
11
# but WITHOUT ANY WARRANTY; without even the implied warranty of
12
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
# GNU General Public License for more details.
14
#
15
# You should have received a copy of the GNU General Public License
16
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
17

    
18
import logging
19
from textwrap import dedent
20

    
21
from chirp.drivers import yaesu_clone, ft1d
22
from chirp import chirp_common, directory, bitwise
23
from chirp.settings import RadioSettings
24

    
25
LOG = logging.getLogger(__name__)
26

    
27
POWER_LEVELS = [chirp_common.PowerLevel("Low", watts=5),
28
                chirp_common.PowerLevel("Mid", watts=25),
29
                chirp_common.PowerLevel("Hi", watts=50)]
30

    
31
TMODES = ["", "Tone", "TSQL", "DTCS", "TSQL-R", None, None, "Pager", "Cross"]
32
CROSS_MODES = [None, "DTCS->", "Tone->DTCS", "DTCS->Tone"]
33

    
34
MODES = ["FM", "NFM"]
35
STEPS = [0, 5, 6.25, 10, 12.5, 15, 20, 25, 50, 100]  # 0 = auto
36
RFSQUELCH = ["OFF", "S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8"]
37

    
38
# Charset is subset of ASCII + some unknown chars \x80-\x86
39
VALID_CHARS = ["%i" % int(x) for x in range(0, 10)] + \
40
    list(":>=<?@") + \
41
    [chr(x) for x in range(ord("A"), ord("Z") + 1)] + \
42
    list("[\\]_") + \
43
    [chr(x) for x in range(ord("a"), ord("z") + 1)] + \
44
    list("%*+,-/=$ ")
45

    
46
MEM_FORMAT = """
47
#seekto 0xceca;
48
struct {
49
  u8 unknown5;
50
  u8 unknown3;
51
  u8 unknown4:6,
52
     dsqtype:2;
53
  u8 dsqcode;
54
  u8 unknown1[2];
55
  char mycall[10];
56
  u8 unknown2[368];
57
} settings;
58

    
59
#seekto 0xfec9;
60
u8 checksum;
61
"""
62

    
63

    
64
@directory.register
65
class FTM7250Radio(ft1d.FT1Radio):
66
    """Yaesu FTM-7250D"""
67
    BAUD_RATE = 38400
68
    VENDOR = "Yaesu"
69
    MODEL = "FTM-7250D"
70
    VARIANT = "R"
71

    
72
    _model = "AH75M"
73
    _memsize = 65227
74
    _block_lengths = [10, 65217]
75
    _has_vibrate = False
76
    _has_af_dual = False
77

    
78
    _mem_params = (199,            # size of memories array
79
                   199)            # size of flags array
80

    
81
    def check_checksums(self):
82
        try:
83
            super().check_checksums()
84
        except Exception as e:
85
            LOG.exception('IGNORING FAILED CHECKSUMS: %s' % e)
86

    
87
    @classmethod
88
    def get_prompts(cls):
89
        rp = chirp_common.RadioPrompts()
90
        rp.pre_download = _(dedent("""\
91
            1. Turn radio off.
92
            2. Connect cable to MIC Jack.
93
            3. Press and hold in the [MHz(SETUP)] key while turning the radio
94
                 on ("CLONE" will appear on the display).
95
            4. <b>After clicking OK</b>, press the [GM(AMS)] key
96
                 to send image."""))
97
        rp.pre_upload = _(dedent("""\
98
            1. Turn radio off.
99
            2. Connect cable to MIC Jack.
100
            3. Press and hold in the [MHz(SETUP)] key while turning the radio
101
                 on ("CLONE" will appear on the display).
102
            4. Press the [MHz(SETUP)] key
103
                 ("-WAIT-" will appear on the LCD)."""))
104
        return rp
105

    
106
    def process_mmap(self):
107
        mem_format = ft1d.MEM_FORMAT + MEM_FORMAT
108
        self._memobj = bitwise.parse(mem_format % self._mem_params, self._mmap)
109

    
110
    def get_features(self):
111
        rf = chirp_common.RadioFeatures()
112
        rf.has_dtcs_polarity = False
113
        rf.valid_modes = list(MODES)
114
        rf.valid_tmodes = [x for x in TMODES if x is not None]
115
        rf.valid_cross_modes = [x for x in CROSS_MODES if x is not None]
116
        rf.valid_duplexes = list(ft1d.DUPLEX)
117
        rf.valid_tuning_steps = list(STEPS)
118
        rf.valid_bands = [(108000000, 580000000)]
119
        # rf.valid_skips = SKIPS
120
        rf.valid_power_levels = POWER_LEVELS
121
        rf.valid_characters = "".join(VALID_CHARS)
122
        rf.valid_name_length = 8
123
        rf.memory_bounds = (1, 199)
124
        rf.can_odd_split = True
125
        rf.has_ctone = False
126
        rf.has_bank = False
127
        rf.has_bank_names = False
128
        # disable until implemented
129
        rf.has_settings = False
130
        return rf
131

    
132
    def _decode_label(self, mem):
133
        # TODO preserve the unknown \x80-x86 chars?
134
        return str(mem.label).rstrip("\xFF")
135

    
136
    def _encode_label(self, mem):
137
        label = mem.name.rstrip().encode('ascii', 'ignore')
138
        return self._add_ff_pad(label, 16)
139

    
140
    def _encode_charsetbits(self, mem):
141
        # TODO this is a setting to decide if the memory should be displayed
142
        # as a name or frequency. Should we expose this setting to the user
143
        # instead of autoselecting it (and losing their preference)?
144
        if mem.name.rstrip() == '':
145
            return [0x00, 0x00]
146
        return [0x00, 0x80]
147

    
148
    def _decode_power_level(self, mem):
149
        return POWER_LEVELS[mem.power - 1]
150

    
151
    def _encode_power_level(self, mem):
152
        return POWER_LEVELS.index(mem.power) + 1
153

    
154
    def _decode_mode(self, mem):
155
        return MODES[mem.mode_alt]
156

    
157
    def _encode_mode(self, mem):
158
        return MODES.index(mem.mode)
159

    
160
    def _get_tmode(self, mem, _mem):
161
        if _mem.tone_mode > 8:
162
            tmode = "Cross"
163
            mem.cross_mode = CROSS_MODES[_mem.tone_mode - 8]
164
        else:
165
            tmode = TMODES[_mem.tone_mode]
166

    
167
        if tmode == "Pager":
168
            # TODO chirp_common does not allow 'Pager'
169
            #   Expose as a different setting?
170
            mem.tmode = ""
171
        else:
172
            mem.tmode = tmode
173

    
174
    def _set_tmode(self, _mem, mem):
175
        if mem.tmode == "Cross":
176
            _mem.tone_mode = 8 + CROSS_MODES.index(mem.cross_mode)
177
        else:
178
            _mem.tone_mode = TMODES.index(mem.tmode)
179

    
180
    def _set_mode(self, _mem, mem):
181
        _mem.mode_alt = self._encode_mode(mem)
182

    
183
    def get_bank_model(self):
184
        return None
185

    
186
    def _debank(self, mem):
187
        return
188

    
189
    def _checksums(self):
190
        return [yaesu_clone.YaesuChecksum(0x064A, 0x06C8),
191
                yaesu_clone.YaesuChecksum(0x06CA, 0x0748),
192
                yaesu_clone.YaesuChecksum(0x074A, 0x07C8),
193
                yaesu_clone.YaesuChecksum(0x07CA, 0x0848),
194
                yaesu_clone.YaesuChecksum(0x0000, 0xFEC9)]
195

    
196
    def _get_settings(self):
197
        # TODO
198
        top = RadioSettings()
199
        return top
200

    
201
    @classmethod
202
    def _wipe_memory(cls, mem):
203
        mem.set_raw("\x00" * (mem.size() // 8))
204

    
205
    def sync_out(self):
206
        # Need to give enough time for the radio to ACK after writes
207
        self.pipe.timeout = 1
208
        return super(FTM7250Radio, self).sync_out()
(2-2/5)