Skip to content
Open
Show file tree
Hide file tree
Changes from 12 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 46 additions & 8 deletions chirp/drivers/h777.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,14 @@
"""

BF1900_SETTINGS2 = """
#seekto 0x0250;
struct {
u8 stun_code[7];
u8 unused1[1];
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An array of 1 doesn't make sense, so this should just be u8 unused1; .. TBH, bitwise should be warning you about this, so I'll check and see why it's not.

u8 kill_code[7];
u8 unused2[1];
} stunkillsettings;

#seekto 0x03C0;
struct {
u8 unused:6,
Expand All @@ -83,7 +91,10 @@
u8 squelchlevel;
u8 scanmode;
u8 timeouttimer;
u8 unused2[4];
u8 stunFunc;
u8 radioInhibitFunc;
u8 stun;
u8 radioInhibit;
} settings2;
"""

Expand Down Expand Up @@ -140,7 +151,7 @@ def _h777_enter_programming_mode(serial, radio_cls):
ack = serial.read(1)
if ack != CMD_ACK:
raise errors.RadioError("Bad ACK after reading ident")
except:
except Exception:
raise errors.RadioError('No ACK after reading ident')
return ident

Expand Down Expand Up @@ -174,7 +185,7 @@ def _h777_read_block(radio, block_addr, block_size):

serial.write(CMD_ACK)
ack = serial.read(1)
except:
except Exception:
raise errors.RadioError("Failed to read block at %04x" % block_addr)

if ack != CMD_ACK:
Expand All @@ -199,7 +210,7 @@ def _h777_write_block(radio, block_addr, block_size):
# ~0.31s.
if serial.read(1) != CMD_ACK:
raise Exception("No ACK")
except:
except Exception:
raise errors.RadioError("Failed to send block "
"to radio at %04x" % block_addr)

Expand Down Expand Up @@ -516,9 +527,10 @@ def get_settings(self):
current_index=_settings.voicelanguage))
basic.append(rs)

rs = RadioSetting("scan", "Scan",
RadioSettingValueBoolean(_settings.scan))
basic.append(rs)
if self.PROGRAM_CMD == b'PROGRAM':
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be better to check something other than this PROGRAM_CMD, but I see you're mirroring another piece of code here that already does that. So maybe let's circle back and clean up both in a subsequent change.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did as best as I could. Not the most elegant way, I know. Quick and dirty. I just wanted to get rid of "Scan" for radios that do not support it.

rs = RadioSetting("scan", "Scan",
RadioSettingValueBoolean(_settings.scan))
basic.append(rs)

if self._has_scanmodes:
rs = RadioSetting("settings2.scanmode", "Scan mode",
Expand Down Expand Up @@ -664,7 +676,7 @@ class H777TestCase(unittest.TestCase):
def setUp(self):
self.driver = H777Radio(None)
self.testdata = bitwise.parse("lbcd foo[2];",
memmap.MemoryMap("\x00\x00"))
memmap.MemoryMapBytes("\x00\x00"))
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for fixing this to use MemoryMapBytes but this needs to take a byte string (so b"\x00\x00"). But, this little integrated test is not actually run anywhere, the original author was just using it (and I bet it doesn't even work anymore). I'll bytes-ify this string just for consistency, but this too could use a revisit.


def test_decode_tone_dtcs_normal(self):
mode, value, pol = self.driver._decode_tone(8023)
Expand Down Expand Up @@ -829,6 +841,32 @@ def match_model(cls, filedata, filename):
return False


@directory.register
class BF9700Radio(BF1901Radio):
VENDOR = "Baofeng"
MODEL = "BF-9700"
ALIASES = []
IDENT = [b"P320h",
b"P3107" + b"\xF4" + b"AM",
]
_ranges = [
(0x0000, 0x0110),
(0x0250, 0x0260),
(0x02B0, 0x02C0),
(0x03C0, 0x03E0),
]
_memsize = 0x0400

# TODO: Is it 1 watt?
POWER_LEVELS = [chirp_common.PowerLevel("Low", watts=1.00),
chirp_common.PowerLevel("High", watts=10.00)]

@classmethod
def match_model(cls, filedata, filename):
# This model is only ever matched via metadata
return False


@directory.register
class MAVERICKRA100Radio(BFM4Radio):
VENDOR = "Maverick"
Expand Down
151 changes: 103 additions & 48 deletions chirp/drivers/th_uv88.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
from chirp.settings import RadioSettingGroup, RadioSetting, \
RadioSettingValueBoolean, RadioSettingValueList, \
RadioSettingValueString, RadioSettingValueInteger, \
RadioSettingValueFloat, RadioSettings
RadioSettingValueFloat, RadioSettings, RadioSettingValueMap

LOG = logging.getLogger(__name__)

Expand Down Expand Up @@ -113,15 +113,33 @@
u8 bitmap[26]; // one bit for each channel skipped
} chan_skip;

#seekto 0x1680;
struct {
ul32 vhfRxLo;
ul32 vhfRxUp;
ul32 vhfTxLo;
ul32 vhfTxUp;
ul32 200RxLo;
ul32 200RxUp;
ul32 200TxLo;
ul32 200TxUp;
ul32 uhfRxLo;
ul32 uhfRxUp;
ul32 uhfTxLo;
ul32 uhfTxUp;
} RxTxLimits;


#seekto 0x191E;
struct {
u8 unk1:4, //
region:4; // 0x191E Radio Region (read only)
// 0 = Unlocked TX: 136-174 MHz / 400-480 MHz
// 2-3 = Unknown
// 1-2 = Unknown
// 3 = EU TX: 144-146 MHz / 430-440 MHz
// 4 = US TX: 144-148 MHz / 420-450 MHz
// 5-15 = Unknown
// 5-14 = Unknown
// 15 = Factory unlocked state 0xFF
} settings2;

#seekto 0x1940;
Expand All @@ -140,7 +158,7 @@
#seekto 0x2180;
struct fm_chn fm_stations[24];

// #seekto 0x021E0;
// #seekto 0x21E0;
struct {
u8 fmset[4];
} fmmap;
Expand Down Expand Up @@ -214,54 +232,68 @@
sideKey1:4; // side key 1
u8 sideKey2_long:4, // 0x1161 side key 2 Long
sideKey1_long:4; // side key 1 Long
u8 unknownBytes[9]; // 0x1162 - 0x116A
u8 unk1; //
u8 dwchan; // 0x1163 Dual Wait channel select, P2/62 only
u8 unk2:4, //
hndTm:4; // 0x1164 Hand Time seconds, P2/62 only
u8 unk3; // 0x1165
u8 unk4; // 0x1166
u8 unk5; // 0x1167
u8 unk6; // 0x1168
u8 unk7; // 0x1169
u8 unk8:4, //
micLev:4; // 0x116A Mic Gain, 1-8, RA89 only
u8 manDownTm:4, // 0x116B manDown Tm
unk15:3, //
unk9:3, //
manDownSw:1; // manDown Sw
u8 offFreqVoltage : 3, // 0x116C unknown referred to in code but not on
// screen
unk1:1, //
unk10:1, //
sqlLevel : 4; // [05] *OFF, 1-9
u8 beep : 1, // 0x116D [09] *OFF, On
u8 beep : 1, // 0x116D [09] *OFF, On
callKind : 2, // code says 1750,2100,1000,1450 as options
// not on screen
introScreen: 2, // [20] *OFF, Voltage, Char String
unk2:2, //
txChSelect : 1; // [02] *Last CH, Main CH
unk11:1, //
txChSelect : 2; // [02] *Main CH, Last CH, Active CH+Hand Time
// (P2/P62 only)
u8 autoPowOff : 3, // 0x116E not on screen? OFF, 30Min, 1HR, 2HR
unk3:1, //
unk12:1, //
tot : 4; // [11] *OFF, 30 Second, 60 Second, 90 Second,
// ... , 270 Second
u8 unk4:1, // 0x116F
u8 unk13:1, // 0x116F
roger:1, // [14] *OFF, On
dailDef:1, // Unknown - 'Volume, Frequency'
language:1, // English only
endToneElim:2, // *Frequency, 120, 180, 240 (RA89)
unk5:1, //
unk6:1; //
unk14:1, //
unk15:1; //
u8 scanType: 2, // 0x1170 [17] *Off, On, 5s, 10s, 15s, 20s, 25s, 30s
disMode : 2, // [33] *Frequency, Channel, Name
ledMode: 4; // [07] *Off, On, 5s, 10s, 15s, 20s, 25s, 30s
u8 unk7; // 0x1171
u8 unk8; // 0x1172 Has flags to do with logging - factory
// enabled (bits 16,64,128)
u8 unk9; // 0x1173
u8 unk16; // 0x1171
u8 elimTailnoSq:4, // 0x1172 *OFF, On 'Eliminate Squelch Tail When
// No Ctc/Dcs Signaling' checkbox
// Toggles between 0x0 and 0x4
unk17:4; // Has flags to do with logging - factory
// enabled (bits 16,64,128)
u8 unk18; // 0x1173
u8 swAudio : 1, // 0x1174 [19] *OFF, On
radioMoni : 1, // [34] *OFF, On
keylock : 1, // [18] *OFF, On
dualWait : 1, // [06] *OFF, On
unk10:1, //
unk19:1, //
light : 3; // [08] *1, 2, 3, 4, 5, 6, 7
u8 voxSw : 1, // 0x1175 [13] *OFF, On
voxDelay: 4, // *0.5S, 1.0S, 1.5S, 2.0S, 2.5S, 3.0S, 3.5S,
// 4.0S, 4.5S, 5.0S
voxLevel : 3; // [03] *1, 2, 3, 4, 5, 6, 7
u8 unk11:4, // 0x1176
u8 unk20:4, // 0x1176
saveMode : 2, // [16] *OFF, 1:1, 1:2, 1:4
keyMode : 2; // [32] *ALL, PTT, KEY, Key & Side Key
u8 unk12; // 0x1177
u8 unk13; // 0x1178
u8 unk14; // 0x1179
u8 unk21; // 0x1177
u8 unk22; // 0x1178
u8 unk23; // 0x1179
u8 name2[6]; // 0x117A unused
} basicsettings;
"""
Expand Down Expand Up @@ -871,30 +903,53 @@ def get_settings(self):

# Menu 02 - TX Channel Select
if self._hasLCD:
options = ["Last Channel", "Main Channel"]
rx = RadioSettingValueList(
options, current_index=_settings.txChSelect)
rset = RadioSetting("basicsettings.txChSelect",
"Priority Transmit", rx)
basic.append(rset)
options = ["Main Channel", "Last Channel"]
else:
options = ["Main Channel", "Last Channel",
"Active Channel with Lockout Time"]
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you sure all h777-based radios without LCD support this extra setting? Or do you want to add that only for the subset of models you know have it?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops, I just realized this is the second driver, so this comment makes no sense.

I'll look to Jim to review this part since I don't have any of these.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As before, I don't see h777 based radios in th_uv88 file. This setting is for Retevis P62, Retevis P2 and their father - TYT TC29. They have 3 options in OEM software. Third option activates extra "Active Channel with Lockout Time"

rx = RadioSettingValueList(options, current_index=_settings.txChSelect)
rset = RadioSetting("basicsettings.txChSelect",
"Priority Transmit", rx)
basic.append(rset)

if self.MODEL in ["P2", "P62"]:
if 'hndTm' in _settings:
rsv = RadioSettingValueMap(
[(str(x), x) for x in range(16)], _settings.hndTm)
rs = RadioSetting('basicsettings.hndTm',
'Lockout Time (seconds)', rsv)
basic.append(rs)

# Menu 03 - VOX Level
rx = RadioSettingValueInteger(1, 7, _settings.voxLevel + 1)
rset = RadioSetting("basicsettings.voxLevel", "Vox Level", rx)
basic.append(rset)

options = ['0.5S', '1.0S', '1.5S', '2.0S', '2.5S', '3.0S', '3.5S',
'4.0S', '4.5S', '5.0S']
rx = RadioSettingValueList(options, current_index=_settings.voxDelay)
rset = RadioSetting("basicsettings.voxDelay", "VOX Delay", rx)
basic.append(rset)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Help me out... this is just moved from below but otherwise unchanged? Is there some reason it was moved or just accidental?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, purely for cosmetic reasons, so that Vox Level and Vox Delay appear in one place in GUI

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay cool, so this change should be one commit on its own so it's clear that it's just moving (and not changing) anything.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This also moves it into basic from advanced. I assume @KC9HI was mirroring the radio vs. software menu structure which is why this was down below in advanced and not with the other one. So I'll let him comment on whether or not he wants this moved.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It does. But, just FYI, in OEM software all these functions are in one, s.c. "Basic" window


# Menu 05 - Squelch Level
options = ["OFF"] + ["%s" % x for x in range(1, 10)]
rx = RadioSettingValueList(options, current_index=_settings.sqlLevel)
rset = RadioSetting("basicsettings.sqlLevel", "Squelch Level", rx)
basic.append(rset)

# Menu 06 - Dual Wait
if self._hasLCD:
rx = RadioSettingValueBoolean(_settings.dualWait)
rset = RadioSetting("basicsettings.dualWait",
"Dual Wait/Standby", rx)
basic.append(rset)
rx = RadioSettingValueBoolean(_settings.dualWait)
rset = RadioSetting("basicsettings.dualWait",
"Dual Wait/Standby", rx)
basic.append(rset)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This adds the dual wait setting for all models, where it was restricted to only ones with LCDs before. Is this intentional? I have h777-based radios without displays that I'm sure don't have dual wait since they only have a way to select a single channel.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

h777 based radios in uv88 file?


if self.MODEL in ["P2", "P62"]:
if 'dwchan' in _settings:
rsv = RadioSettingValueMap(
[(str(x + 1), x) for x in range(200)], _settings.dwchan)
rs = RadioSetting('basicsettings.dwchan',
'Dual Wait Channel', rsv)
basic.append(rs)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm guessing these non-display radios have a dedicated "always dual wait for channel X" function? Mine aren't in this list, which means they'll get a dual-wait setting in settings, but won't get this extra setting either. I think the logic should be "if LCD, always add dual-wait. If not LCD but dwchan in _settings then add dual-wait and dual-wait-chanel" right?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This and the question above are still relevant to make sure you intended to change this in this way, however my confidence about whether it's correct or not is irrelevant since I don't know anything about these radios.

@KC9HI can you review the changes being made to this driver to make sure they jive with your understanding of them all?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, in Retevis P62/P2 and TYT TC29, when dual wait is activated, you tell the radio which other, permanently set channel to watch. Range 1 to 200. Dan, you did this piece when we spoke for the first time. See #11864

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, and I remember it being too wide a scope in terms of the models it affected, hence it being reverted and hence me asking for someone more familiar with these models to help review :)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nah, it's just two models. Almost identical. One with display - Retevis RA89/TYT TH-UV99, the other one without - Retevis P62/P2/ TYT TC29.
I've double and triple checked all functions on my radios, everything is working as expected. I have a park of all of these. Found a few firmware bugs too along the way.
I keep filing on this driver and it would be extremely cool if @KC9HI could revisit it. I have a bunch of question to ask


# Menu 07 - LED Mode
if self._hasLCD:
Expand Down Expand Up @@ -1003,6 +1058,13 @@ def get_settings(self):
advanced = RadioSettingGroup("advanced", "Advanced Settings")
group.append(advanced)

# Menu 38 - Mic Level
if self.MODEL == "RA89":
rsv = RadioSettingValueMap(
[(str(x + 1), x) for x in range(8)], _settings.micLev)
rs = RadioSetting('basicsettings.micLev', 'Mic Level', rsv)
basic.append(rs)

# software only
if self.MODEL in ["RA89", "P2", "P62"]:
options = ['Frequency', '120', '180', '240']
Expand Down Expand Up @@ -1059,19 +1121,13 @@ def _char_to_name(name):
advanced.append(rset)

# software only
options = ['0.5S', '1.0S', '1.5S', '2.0S', '2.5S', '3.0S', '3.5S',
'4.0S', '4.5S', '5.0S']
rx = RadioSettingValueList(options, current_index=_settings.voxDelay)
rset = RadioSetting("basicsettings.voxDelay", "VOX Delay", rx)
advanced.append(rset)

options = ['Unlocked', 'Unknown 1', 'Unknown 2', 'EU', 'US']
# extend option list with unknown description for values 5 - 15.
for ix in range(len(options), _settings2.region + 1):
item_to_add = 'Unknown {region_code}'.format(region_code=ix)
options.append(item_to_add)
# log unknown region codes greater than 4
if _settings2.region > 4:
options = ['Unlocked', 'Unknown 1', 'Unknown 2', 'EU', 'US',
'Unknown 3', 'Unknown 4', 'Unknown 5', 'Unknown 6',
'Unknown 7', 'Unknown 8', 'Unknown 9', 'Unknown 10',
'Unknown 11', 'Unknown 12', 'Factory']
# log unknown region codes greater than 4 and less than 15
# codes for Canada and Taiwan exist, need to be determined
if _settings2.region > 4 and _settings2.region < 15:
LOG.debug("Unknown region code: {value}".
format(value=_settings2.region))
rx = RadioSettingValueList(options, current_index=_settings2.region)
Expand Down Expand Up @@ -1229,7 +1285,6 @@ def myset_freq(setting, obj, atrb, mult):

def set_settings(self, settings):
_settings = self._memobj.basicsettings
_mem = self._memobj
for element in settings:
if not isinstance(element, RadioSetting):
self.set_settings(element)
Expand Down
Loading