Skip to content

Commit

Permalink
Fixed decoding of roaming channels. Fixes #319.
Browse files Browse the repository at this point in the history
 For AT-D878UV, AT-D878UV II, AT-D578UV, DMR-6X2UV. (#321)
  • Loading branch information
hmatuschek authored Mar 15, 2023
1 parent 48dbd5c commit 432e064
Show file tree
Hide file tree
Showing 7 changed files with 247 additions and 46 deletions.
73 changes: 42 additions & 31 deletions lib/d878uv_codeplug.cc
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,7 @@ D878UVCodeplug::RoamingChannelElement::RoamingChannelElement(uint8_t *ptr, unsig
}

D878UVCodeplug::RoamingChannelElement::RoamingChannelElement(uint8_t *ptr)
: Element(ptr, 0x0020)
: Element(ptr, RoamingChannelElement::size())
{
// pass...
}
Expand All @@ -292,41 +292,41 @@ D878UVCodeplug::RoamingChannelElement::clear() {

unsigned
D878UVCodeplug::RoamingChannelElement::rxFrequency() const {
return getBCD8_be(Offsets::RXFrequency)*10;
return getBCD8_be(Offset::rxFrequency())*10;
}
void
D878UVCodeplug::RoamingChannelElement::setRXFrequency(unsigned hz) {
setBCD8_be(Offsets::RXFrequency, hz/10);
setBCD8_be(Offset::rxFrequency(), hz/10);
}
unsigned
D878UVCodeplug::RoamingChannelElement::txFrequency() const {
return getBCD8_be(Offsets::TXFrequency)*10;
return getBCD8_be(Offset::txFrequency())*10;
}
void
D878UVCodeplug::RoamingChannelElement::setTXFrequency(unsigned hz) {
setBCD8_be(Offsets::TXFrequency, hz/10);
setBCD8_be(Offset::txFrequency(), hz/10);
}

bool
D878UVCodeplug::RoamingChannelElement::hasColorCode() const {
return ColorCodeValue::Disabled == getUInt8(Offsets::ColorCode);
return ColorCodeValue::Disabled == getUInt8(Offset::colorCode());
}
unsigned
D878UVCodeplug::RoamingChannelElement::colorCode() const {
return std::min(15u, (unsigned)getUInt8(Offsets::ColorCode));
return std::min(15u, (unsigned)getUInt8(Offset::colorCode()));
}
void
D878UVCodeplug::RoamingChannelElement::setColorCode(unsigned cc) {
setUInt8(Offsets::ColorCode, cc);
setUInt8(Offset::colorCode(), cc);
}
void
D878UVCodeplug::RoamingChannelElement::disableColorCode() {
setUInt8(Offsets::ColorCode, ColorCodeValue::Disabled);
setUInt8(Offset::colorCode(), ColorCodeValue::Disabled);
}

DMRChannel::TimeSlot
D878UVCodeplug::RoamingChannelElement::timeSlot() const {
switch (getUInt8(Offsets::TimeSlot)) {
switch (getUInt8(Offset::timeSlot())) {
case TimeSlotValue::TS1: return DMRChannel::TimeSlot::TS1;
case TimeSlotValue::TS2: return DMRChannel::TimeSlot::TS2;
}
Expand All @@ -335,18 +335,18 @@ D878UVCodeplug::RoamingChannelElement::timeSlot() const {
void
D878UVCodeplug::RoamingChannelElement::setTimeSlot(DMRChannel::TimeSlot ts) {
switch (ts) {
case DMRChannel::TimeSlot::TS1: setUInt8(Offsets::TimeSlot, TimeSlotValue::TS1); break;
case DMRChannel::TimeSlot::TS2: setUInt8(Offsets::TimeSlot, TimeSlotValue::TS2); break;
case DMRChannel::TimeSlot::TS1: setUInt8(Offset::timeSlot(), TimeSlotValue::TS1); break;
case DMRChannel::TimeSlot::TS2: setUInt8(Offset::timeSlot(), TimeSlotValue::TS2); break;
}
}

QString
D878UVCodeplug::RoamingChannelElement::name() const {
return readASCII(Offsets::Name, Offsets::NameLength, 0x00);
return readASCII(Offset::name(), Limit::nameLength(), 0x00);
}
void
D878UVCodeplug::RoamingChannelElement::setName(const QString &name) {
writeASCII(Offsets::Name, name, Offsets::NameLength, 0x00);
writeASCII(Offset::name(), name, Limit::nameLength(), 0x00);
}

bool
Expand Down Expand Up @@ -391,15 +391,15 @@ D878UVCodeplug::RoamingZoneElement::RoamingZoneElement(uint8_t *ptr, unsigned si
}

D878UVCodeplug::RoamingZoneElement::RoamingZoneElement(uint8_t *ptr)
: Element(ptr, 0x0080)
: Element(ptr, RoamingZoneElement::size())
{
// pass...
}

void
D878UVCodeplug::RoamingZoneElement::clear() {
memset(_data, 0x00, _size);
memset(_data+0x000, 0xff, NUM_CH_PER_ROAMINGZONE);
memset(_data+Offset::members(), 0xff, Limit::numMembers());
}

bool
Expand All @@ -408,49 +408,60 @@ D878UVCodeplug::RoamingZoneElement::hasMember(unsigned n) const {
}
unsigned
D878UVCodeplug::RoamingZoneElement::member(unsigned n) const {
return getUInt8(0x0000 + n);
return getUInt8(Offset::members() + n*Offset::betweenMembers());
}
void
D878UVCodeplug::RoamingZoneElement::setMember(unsigned n, unsigned idx) {
setUInt8(0x0000 + n, idx);
if (n >= Limit::numMembers())
return;
setUInt8(Offset::members() + n*Offset::betweenMembers(), idx);
}
void
D878UVCodeplug::RoamingZoneElement::clearMember(unsigned n) {
setMember(n, 0xff);
if (n >= Limit::numMembers())
return;
setMember(Offset::members() + n*Offset::betweenMembers(), 0xff);
}

QString
D878UVCodeplug::RoamingZoneElement::name() const {
return readASCII(0x0040, 16, 0x00);
return readASCII(Offset::name(), Limit::nameLength(), 0x00);
}
void
D878UVCodeplug::RoamingZoneElement::setName(const QString &name) {
writeASCII(0x0040, name, 16, 0x00);
writeASCII(Offset::name(), name, Limit::nameLength(), 0x00);
}

bool
D878UVCodeplug::RoamingZoneElement::fromRoamingZone(
RoamingZone *zone, Context &ctx)
D878UVCodeplug::RoamingZoneElement::fromRoamingZone(RoamingZone *zone, Context &ctx, const ErrorStack& err)
{
Q_UNUSED(err)

clear();
setName(zone->name());
for (int i=0; i<std::min(NUM_CH_PER_ROAMINGZONE, zone->count()); i++) {
for (unsigned int i=0; i<std::min(Limit::numMembers(), (unsigned int)zone->count()); i++) {
setMember(i, ctx.index(zone->channel(i)));
}
return true;
}

RoamingZone *
D878UVCodeplug::RoamingZoneElement::toRoamingZone() const {
D878UVCodeplug::RoamingZoneElement::toRoamingZone(Context &ctx, const ErrorStack &err) const {
Q_UNUSED(ctx); Q_UNUSED(err);
return new RoamingZone(name());
}

bool
D878UVCodeplug::RoamingZoneElement::linkRoamingZone(RoamingZone *zone, Context &ctx)
D878UVCodeplug::RoamingZoneElement::linkRoamingZone(RoamingZone *zone, Context &ctx, const ErrorStack &err)
{
for (uint8_t i=0; (i<NUM_CH_PER_ROAMINGZONE)&&hasMember(i); i++) {
if (ctx.has<RoamingChannel>(i))
zone->addChannel(ctx.get<RoamingChannel>(i));
for (uint8_t i=0; (i<Limit::numMembers())&&hasMember(i); i++) {
if (ctx.has<RoamingChannel>(member(i))) {
zone->addChannel(ctx.get<RoamingChannel>(member(i)));
} else {
errMsg(err) << "Cannot link roaming zone '" << zone->name()
<< "': Roaming channel index " << member(i) << " is not defined.";
return false;
}
}
return true;
}
Expand Down Expand Up @@ -2579,9 +2590,9 @@ D878UVCodeplug::createRoaming(Context &ctx, const ErrorStack &err) {
continue;
uint32_t addr = ADDR_ROAMING_ZONE_0 + i*ROAMING_ZONE_OFFSET;
RoamingZoneElement z(data(addr));
RoamingZone *zone = z.toRoamingZone();
RoamingZone *zone = z.toRoamingZone(ctx, err);
ctx.config()->roamingZones()->add(zone); ctx.add(zone, i);
z.linkRoamingZone(zone, ctx);
z.linkRoamingZone(zone, ctx, err);
}

return true;
Expand Down
57 changes: 44 additions & 13 deletions lib/d878uv_codeplug.hh
Original file line number Diff line number Diff line change
Expand Up @@ -1125,16 +1125,6 @@ public:
RoamingChannelElement(uint8_t *ptr, unsigned size);

protected:
/** Address offsets within the element. */
enum Offsets {
RXFrequency = 0x0000,
TXFrequency = 0x0004,
ColorCode = 0x0008,
TimeSlot = 0x0009,
Name = 0x000a,
NameLength = 16
};

/** Special values for the color code. */
enum ColorCodeValue {
Disabled = 16
Expand All @@ -1150,6 +1140,9 @@ public:
/** Constructor. */
RoamingChannelElement(uint8_t *ptr);

/** The size of the element. */
static constexpr unsigned int size() { return 0x0020; }

/** Resets the roaming channel. */
void clear();

Expand Down Expand Up @@ -1185,6 +1178,24 @@ public:
virtual bool fromChannel(const RoamingChannel *ch);
/** Constructs a @c RoamingChannel instance for this roaming channel. */
virtual RoamingChannel *toChannel(Context &ctx);

public:
/** Some limits. */
struct Limit {
static constexpr unsigned int nameLength() { return 16; } ///< Maximum name length.
};

protected:
/** Some internal offsets within the element. */
struct Offset {
/// @cond DO_NOT_DOCUMENT
static constexpr unsigned int rxFrequency() { return 0x0000; }
static constexpr unsigned int txFrequency() { return 0x0004; }
static constexpr unsigned int colorCode() { return 0x0008; }
static constexpr unsigned int timeSlot() { return 0x0009; }
static constexpr unsigned int name() { return 0x000a; }
/// @endcond
};
};

/** Represents a roaming zone within the binary codeplug.
Expand All @@ -1201,6 +1212,9 @@ public:
/** Constructor. */
RoamingZoneElement(uint8_t *ptr);

/** The size of the element. */
static constexpr unsigned int size() { return 0x0080; }

/** Clears the roaming zone. */
void clear();

Expand All @@ -1219,11 +1233,28 @@ public:
virtual void setName(const QString &name);

/** Assembles a binary representation of the given RoamingZone instance.*/
virtual bool fromRoamingZone(RoamingZone *zone, Context& ctx);
virtual bool fromRoamingZone(RoamingZone *zone, Context& ctx, const ErrorStack &err=ErrorStack());
/** Constructs a @c RoamingZone instance from this configuration. */
virtual RoamingZone *toRoamingZone() const;
virtual RoamingZone *toRoamingZone(Context& ctx, const ErrorStack &err=ErrorStack()) const;
/** Links the given RoamingZone. */
virtual bool linkRoamingZone(RoamingZone *zone, Context& ctx);
virtual bool linkRoamingZone(RoamingZone *zone, Context& ctx, const ErrorStack& err=ErrorStack());

public:
/** Some limits. */
struct Limit {
static constexpr unsigned int nameLength() { return 16; } ///< Maximum name length.
static constexpr unsigned int numMembers() { return 64; } ///< Maximum number of roaming channel in zone.
};

protected:
/** Some internal offsets within the element. */
struct Offset {
/// @cond DO_NOT_DOCUMENT
static constexpr unsigned int members() { return 0x0000; }
static constexpr unsigned int betweenMembers() { return 0x0001; }
static constexpr unsigned int name() { return 0x0040; }
/// @endcond
};
};

/** Represents an AES encryption key.
Expand Down
4 changes: 2 additions & 2 deletions lib/dmr6x2uv_codeplug.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1032,9 +1032,9 @@ DMR6X2UVCodeplug::createRoaming(Context &ctx, const ErrorStack &err) {
continue;
uint32_t addr = ADDR_ROAMING_ZONE_0 + i*ROAMING_ZONE_OFFSET;
D878UVCodeplug::RoamingZoneElement z(data(addr));
RoamingZone *zone = z.toRoamingZone();
RoamingZone *zone = z.toRoamingZone(ctx, err);
ctx.config()->roamingZones()->add(zone); ctx.add(zone, i);
z.linkRoamingZone(zone, ctx);
z.linkRoamingZone(zone, ctx, err);
}

return true;
Expand Down
37 changes: 37 additions & 0 deletions test/d878uv_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ D878UVTest::initTestCase() {
QFAIL(QString("Cannot open codeplug file: %1")
.arg(err.format()).toStdString().c_str());
}
if (! _roamingConfig.readYAML(":/data/roaming_channel_test.yaml", err)) {
QFAIL(QString("Cannot open codeplug file: %1")
.arg(err.format()).toStdString().c_str());
}
}

void
Expand Down Expand Up @@ -55,5 +59,38 @@ D878UVTest::testBasicConfigDecoding() {
}
}

void
D878UVTest::testRoaming() {
ErrorStack err;
Codeplug::Flags flags; flags.updateCodePlug=false;
D878UVCodeplug codeplug;
if (! codeplug.encode(&_roamingConfig, flags, err)) {
QFAIL(QString("Cannot encode codeplug for AnyTone AT-D878UV: {}")
.arg(err.format()).toStdString().c_str());
}

Config config;
if (! codeplug.decode(&config, err)) {
QFAIL(QString("Cannot decode codeplug for AnyTone AT-D878UV: {}")
.arg(err.format()).toStdString().c_str());
}

QCOMPARE(config.roamingChannels()->count(), 3);
QCOMPARE(config.roamingZones()->count(), 2);
QCOMPARE(config.roamingZones()->count(), 2);

QCOMPARE(config.roamingZones()->get(0)->as<RoamingZone>()->count(), 2);
QCOMPARE(config.roamingZones()->get(0)->as<RoamingZone>()->channel(0),
config.roamingChannels()->get(0)->as<RoamingChannel>());
QCOMPARE(config.roamingZones()->get(0)->as<RoamingZone>()->channel(1),
config.roamingChannels()->get(1)->as<RoamingChannel>());

QCOMPARE(config.roamingZones()->get(1)->as<RoamingZone>()->count(), 2);
QCOMPARE(config.roamingZones()->get(1)->as<RoamingZone>()->channel(0),
config.roamingChannels()->get(0)->as<RoamingChannel>());
QCOMPARE(config.roamingZones()->get(1)->as<RoamingZone>()->channel(1),
config.roamingChannels()->get(2)->as<RoamingChannel>());
}

QTEST_GUILESS_MAIN(D878UVTest)

3 changes: 3 additions & 0 deletions test/d878uv_test.hh
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,11 @@ private slots:
void testBasicConfigEncoding();
void testBasicConfigDecoding();

void testRoaming();

protected:
Config _basicConfig;
Config _roamingConfig;
};

#endif // D878UVTEST_HH
Loading

0 comments on commit 432e064

Please sign in to comment.