Project

General

Profile

New Model #1107 » ft1802.py

FT-1900 driver - Tom Hayward, 03/19/2014 10:48 AM

 
# Copyright 2012 Tom Hayward <tom@tomh.us>
#
# 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/>.

# FT-1802 Clone Proceedure
# 1. Turn radio off.
# 2. Connect cable to mic jack.
# 3. Press and hold in the [LOW(A/N)] key while turning the radio on.
# 4. In Chirp, choose Download from Radio.
# 5. Press the [MHz(SET)] key to send image.
# or
# 4. Press the [D/MR(MW)] key ("--WAIT--" will appear on the LCD).
# 5. In Chirp, choose Upload to Radio.

from chirp import chirp_common, bitwise, directory, yaesu_clone
from chirp.settings import RadioSetting, RadioSettingGroup, \
RadioSettingValueBoolean
from textwrap import dedent

MEM_FORMAT = """
#seekto 0x06ea;
struct {
u8 odd_pskip:1,
odd_skip:1,
odd_visible:1,
odd_valid:1,
even_pskip:1,
even_skip:1,
even_visible:1,
even_valid:1;
} flags[100];

#seekto 0x076a;
struct {
u8 unknown1a:1,
step_changed:1,
narrow:1,
clk_shift:1,
unknown1b:4;
u8 unknown2a:2,
duplex:2,
unknown2b:1,
tune_step:3;
bbcd freq[3];
u8 power:2,
unknown3:3,
tmode:3;
u8 name[6];
bbcd offset[3];
u8 tone;
u8 dtcs;
u8 unknown4;
} memory[200];
"""


MODES = ["FM", "NFM"]
TMODES = ["", "Tone", "TSQL", "DTCS", "TSQL-R", "Cross"]
CROSS_MODES = ["DTCS->", "Tone->DTCS", "DTCS->Tone"]
DUPLEX = ["", "-", "+", "split"]
STEPS = [5.0, 10.0, 12.5, 15.0, 20.0, 25.0, 50.0, 100.0]
POWER_LEVELS = [chirp_common.PowerLevel("LOW1", watts=5),
chirp_common.PowerLevel("LOW2", watts=10),
chirp_common.PowerLevel("LOW3", watts=25),
chirp_common.PowerLevel("HIGH", watts=50),
]
CHARSET = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ +-/?()?_"


@directory.register
class FT1802Radio(yaesu_clone.YaesuCloneModeRadio):
"""Yaesu FT-1802"""
VENDOR = "Yaesu"
MODEL = "FT-1802M"
BAUD_RATE = 19200

_model = "AH023"
_block_lengths = [10, 8001]
_memsize = 8011

@classmethod
def get_prompts(cls):
rp = chirp_common.RadioPrompts()
rp.pre_download = _(dedent("""\
1. Turn radio off.
2. Connect cable to mic jack.
3. Press and hold in the [LOW(A/N)] key while turning the radio on.
4. <b>After clicking OK</b>, press the [MHz(SET)] key to send image."""))
rp.pre_upload = _(dedent("""\
1. Turn radio off.
2. Connect cable to mic jack.
3. Press and hold in the [LOW(A/N)] key while turning the radio on.
4. Press the [D/MR(MW)] key ("--WAIT--" will appear on the LCD)."""))
return rp

def get_features(self):
rf = chirp_common.RadioFeatures()

rf.memory_bounds = (0, 199)

rf.can_odd_split = True
rf.has_ctone = False
rf.has_tuning_step = True
rf.has_dtcs_polarity = False # in radio settings, not per memory
rf.has_bank = False # has banks, but not implemented

rf.valid_tuning_steps = STEPS
rf.valid_modes = MODES
rf.valid_tmodes = TMODES
rf.valid_bands = [(137000000, 174000000)]
rf.valid_power_levels = POWER_LEVELS
rf.valid_duplexes = DUPLEX
rf.valid_skips = ["", "S", "P"]
rf.valid_name_length = 6
rf.valid_characters = CHARSET
rf.has_cross = True
rf.valid_cross_modes = list(CROSS_MODES)

return rf

def _checksums(self):
return [yaesu_clone.YaesuChecksum(0, self._memsize-2)]

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

def get_raw_memory(self, number):
return repr(self._memobj.memory[number]) + \
repr(self._memobj.flags[number/2])

def get_memory(self, number):
_mem = self._memobj.memory[number]
_flag = self._memobj.flags[number/2]

nibble = (number % 2) and "odd" or "even"
visible = _flag["%s_visible" % nibble]
valid = _flag["%s_valid" % nibble]
pskip = _flag["%s_pskip" % nibble]
skip = _flag["%s_skip" % nibble]

mem = chirp_common.Memory()
mem.number = number

if not visible:
mem.empty = True
if not valid:
mem.empty = True
return mem

mem.freq = chirp_common.fix_rounded_step(int(_mem.freq) * 1000)
mem.offset = chirp_common.fix_rounded_step(int(_mem.offset) * 1000)
mem.duplex = DUPLEX[_mem.duplex]
mem.tuning_step = _mem.step_changed and STEPS[_mem.tune_step] or STEPS[0]
if _mem.tmode < TMODES.index("Cross"):
mem.tmode = TMODES[_mem.tmode]
mem.cross_mode = CROSS_MODES[0]
else:
mem.tmode = "Cross"
mem.cross_mode = CROSS_MODES[_mem.tmode - TMODES.index("Cross")]
mem.rtone = chirp_common.TONES[_mem.tone]
mem.dtcs = chirp_common.DTCS_CODES[_mem.dtcs]
for i in _mem.name:
if i == 0xFF:
break
if i & 0x80 == 0x80:
# first bit in name is "show name"
mem.name += CHARSET[0x80 ^ int(i)]
else:
mem.name += CHARSET[i]
mem.name = mem.name.rstrip()
mem.mode = _mem.narrow and "NFM" or "FM"
mem.skip = pskip and "P" or skip and "S" or ""
mem.power = POWER_LEVELS[_mem.power]

mem.extra = RadioSettingGroup("extra", "Extra Settings")
rs = RadioSetting("clk_shift", "Clock Shift",
RadioSettingValueBoolean(_mem.clk_shift))
mem.extra.append(rs)

return mem

def set_memory(self, mem):
_mem = self._memobj.memory[mem.number]
_flag = self._memobj.flags[mem.number/2]

nibble = (mem.number % 2) and "odd" or "even"
valid = _flag["%s_valid" % nibble]
visible = _flag["%s_visible" % nibble]

if not mem.empty and not valid:
_flag["%s_valid" % nibble] = True
_mem.unknown1a = 0x00
_mem.clk_shift = 0x00
_mem.unknown1b = 0x00
_mem.unknown2a = 0x00
_mem.unknown2b = 0x00
_mem.unknown3 = 0x00
_mem.unknown4 = 0x00

if mem.empty and valid and not visible:
_flag["%s_valid" % nibble] = False
return
_flag["%s_visible" % nibble] = not mem.empty

if mem.empty:
return

_flag["%s_valid" % nibble] = True

_mem.freq = mem.freq / 1000
_mem.offset = mem.offset / 1000
_mem.duplex = DUPLEX.index(mem.duplex)
_mem.tune_step = STEPS.index(mem.tuning_step)
_mem.step_changed = mem.tuning_step != STEPS[0]
if mem.tmode != "Cross":
_mem.tmode = TMODES.index(mem.tmode)
else:
_mem.tmode = TMODES.index("Cross") + CROSS_MODES.index(mem.cross_mode)
_mem.tone = chirp_common.TONES.index(mem.rtone)
_mem.dtcs = chirp_common.DTCS_CODES.index(mem.dtcs)

_mem.name = [0xFF] * 6
for i in range(0, len(mem.name)):
try:
_mem.name[i] = CHARSET.index(mem.name[i])
except IndexError:
raise Exception("Character `%s' not supported")
if _mem.name[0] != 0xFF:
_mem.name[0] += 0x80 # show name instead of frequency

_mem.narrow = MODES.index(mem.mode)
_mem.power = 3 if mem.power is None else POWER_LEVELS.index(mem.power)

_flag["%s_pskip" % nibble] = mem.skip == "P"
_flag["%s_skip" % nibble] = mem.skip == "S"

for element in mem.extra:
setattr(_mem, element.get_name(), element.value)


FT1900_MEM_FORMAT = """
#seekto 0x36A;
struct {
u8 bname[6];
} bank_names[8];

#seekto 0x3CE;
struct {
u16 bitmap[50];
} bank_channels[8];

#seekto 0x06ee;
struct {
u8 odd_pskip:1,
odd_skip:1,
odd_visible:1,
odd_valid:1,
even_pskip:1,
even_skip:1,
even_visible:1,
even_valid:1;
} flags[100];

#seekto 0x076E;
struct {
u8 unknown1a:1,
step_changed:1,
narrow:1,
clk_shift:1,
unknown1b:4;
u8 unknown2a:2,
duplex:2,
unknown2b:1,
tune_step:3;
bbcd freq[3];
u8 power:2,
unknown3:3,
tmode:3;
u8 name[6];
bbcd offset[3];
u8 tone;
u8 dtcs;
u8 unknown4;
u8 ttone;
u8 unknown5;
} memory[200];
"""

@directory.register
class FT1900Radio(FT1802Radio):
"""Yaesu FT-1900"""
VENDOR = "Yaesu"
MODEL = "FT-1900R"
BAUD_RATE = 19200

_model = "VC23\x00"
_block_lengths = [14, 8001]

def process_mmap(self):
self._memobj = bitwise.parse(FT1900_MEM_FORMAT, self._mmap)
(4-4/4)