diff --git a/doc/code/d878uv_channel.txt b/doc/code/d878uv_channel.txt index eccd9ca8..744c0e59 100644 --- a/doc/code/d878uv_channel.txt +++ b/doc/code/d878uv_channel.txt @@ -24,7 +24,7 @@ +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ 38 | GPS System Index | Freq. corr. signed in 10Hz | DMR encryption idx +1, 0=off | 0 0 0 0 0 |SMF|RnK|Muk| +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ -3c | Unused set to 0x00000000 | +3c | FM APRS Frequency index | Unused set to 0x000000 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ Field description: diff --git a/lib/anytone_codeplug.cc b/lib/anytone_codeplug.cc index 24820b29..c3015623 100644 --- a/lib/anytone_codeplug.cc +++ b/lib/anytone_codeplug.cc @@ -4545,7 +4545,15 @@ AnytoneCodeplug::index(Config *config, Context &ctx, const ErrorStack &err) cons if (config->posSystems()->system(i)->is()) { ctx.add(config->posSystems()->system(i)->as(), d); d++; } else if (config->posSystems()->system(i)->is()) { - ctx.add(config->posSystems()->system(i)->as(), a); a++; + auto *aprs = config->posSystems()->system(i)->as(); + ctx.add(aprs, a); a++; + // Index FM APRS frequencies (referenced in channel extensions). + if (auto *ext = aprs->anytoneExtension()) { + for (int j=0; jfrequencies()->count(); j++) { + // Index 0 = default, so index is 1-based + ctx.add(ext->frequencies()->get(j)->as(), j+1); + } + } } } @@ -4570,6 +4578,8 @@ AnytoneCodeplug::encode(Config *config, const Flags &flags, const ErrorStack &er Context ctx(config); // Register table for auto-repeater offsets ctx.addTable(&AnytoneAutoRepeaterOffset::staticMetaObject); + // Register table for FM APRS frequencies + ctx.addTable(&AnytoneAPRSFrequency::staticMetaObject); if (! index(config, ctx, err)) { errMsg(err) << "Cannot encode anytone codeplug."; @@ -4597,8 +4607,11 @@ bool AnytoneCodeplug::decode(Config *config, const ErrorStack &err) { // Maps code-plug indices to objects Context ctx(config); + // Register table for auto-repeater offsets ctx.addTable(&AnytoneAutoRepeaterOffset::staticMetaObject); + // Register table for FM APRS frequencies + ctx.addTable(&AnytoneAPRSFrequency::staticMetaObject); return this->decodeElements(ctx, err); } diff --git a/lib/anytone_codeplug.hh b/lib/anytone_codeplug.hh index eedea74a..30369508 100644 --- a/lib/anytone_codeplug.hh +++ b/lib/anytone_codeplug.hh @@ -372,6 +372,12 @@ public: virtual bool linkChannelObj(Channel *c, Context &ctx) const; /** Initializes this codeplug channel from the given generic configuration. */ virtual bool fromChannelObj(const Channel *c, Context &ctx); + + protected: + /** Internal used offsets within the channel element. */ + struct Offset { + /// @todo Implement + }; }; /** Represents the channel bitmaps in all AnyTone codeplugs. */ diff --git a/lib/anytone_extension.cc b/lib/anytone_extension.cc index c747456e..cdcc4bfd 100644 --- a/lib/anytone_extension.cc +++ b/lib/anytone_extension.cc @@ -1,11 +1,70 @@ #include "anytone_extension.hh" #include "signaling.hh" +/* ********************************************************************************************* * + * Implementation of AnytoneAPRSFrequency + * ********************************************************************************************* */ +AnytoneAPRSFrequency::AnytoneAPRSFrequency(QObject *parent) + : ConfigObject(parent), _frequency(Frequency::fromHz(0)) +{ + // pass... +} + +ConfigItem * +AnytoneAPRSFrequency::clone() const { + AnytoneAPRSFrequency *obj = new AnytoneAPRSFrequency(); + if (! obj->copy(*this)) { + obj->deleteLater(); + return nullptr; + } + return obj; +} + +Frequency +AnytoneAPRSFrequency::frequency() const { + return _frequency; +} +void +AnytoneAPRSFrequency::setFrequency(Frequency freq) { + if (_frequency == freq) + return; + _frequency = freq; + emit modified(this); +} + + +/* ********************************************************************************************* * + * Implementation of AnytoneAPRSFrequencyRef + * ********************************************************************************************* */ +AnytoneAPRSFrequencyRef::AnytoneAPRSFrequencyRef(QObject *parent) + : ConfigObjectReference(AnytoneAPRSFrequency::staticMetaObject, parent) +{ + // pass... +} + + +/* ********************************************************************************************* * + * Implementation of AnytoneAPRSFrequencyList + * ********************************************************************************************* */ +AnytoneAPRSFrequencyList::AnytoneAPRSFrequencyList(QObject *parent) + : ConfigObjectList(AnytoneAPRSFrequency::staticMetaObject, parent) +{ + // pass... +} + +ConfigItem * +AnytoneAPRSFrequencyList::allocateChild(const YAML::Node &node, ConfigItem::Context &ctx, const ErrorStack &err) { + Q_UNUSED(node); Q_UNUSED(ctx); Q_UNUSED(err); + return new AnytoneAPRSFrequency(); +} + + /* ********************************************************************************************* * * Implementation of AnytoneChannelExtension * ********************************************************************************************* */ AnytoneChannelExtension::AnytoneChannelExtension(QObject *parent) - : ConfigExtension(parent), _talkaround(false), _frequencyCorrection(0), _handsFree(false) + : ConfigExtension(parent), _talkaround(false), _frequencyCorrection(0), _handsFree(false), + _fmAPRSFrequency(new AnytoneAPRSFrequencyRef(this)) { // pass... } @@ -46,6 +105,11 @@ AnytoneChannelExtension::enableHandsFree(bool enable) { emit modified(this); } +AnytoneAPRSFrequencyRef * +AnytoneChannelExtension::fmAPRSFrequency() const { + return _fmAPRSFrequency; +} + /* ********************************************************************************************* * * Implementation of AnytoneAnalogChannelExtension @@ -2683,7 +2747,8 @@ AnytoneFMAPRSSettingsExtension::AnytoneFMAPRSSettingsExtension(QObject *parent) : ConfigExtension(parent), _txDelay(Interval::fromMilliseconds(60)), _preWaveDelay(Interval::fromMilliseconds(0)), _passAll(false), _reportPosition(false), _reportMicE(false), _reportObject(false), _reportItem(false), _reportMessage(false), - _reportWeather(false), _reportNMEA(false), _reportStatus(false), _reportOther(false) + _reportWeather(false), _reportNMEA(false), _reportStatus(false), _reportOther(false), + _frequencies(new AnytoneAPRSFrequencyList(this)) { // pass... } @@ -2842,3 +2907,7 @@ AnytoneFMAPRSSettingsExtension::enableReportOther(bool enable) { emit modified(this); } +AnytoneAPRSFrequencyList * +AnytoneFMAPRSSettingsExtension::frequencies() const { + return _frequencies; +} diff --git a/lib/anytone_extension.hh b/lib/anytone_extension.hh index 9bcc5728..a50a0ad2 100644 --- a/lib/anytone_extension.hh +++ b/lib/anytone_extension.hh @@ -10,6 +10,62 @@ #include +/** Implements the config representation of an FM APRS frequency. + * @ingroup anytone */ +class AnytoneAPRSFrequency: public ConfigObject +{ + Q_OBJECT + + Q_CLASSINFO("IdPrefix", "af") + + Q_CLASSINFO("frequencyDecription", + "Transmit-frequency.") + /** The frequency. */ + Q_PROPERTY(Frequency frequency READ frequency WRITE setFrequency) + +public: + /** Default constructor. */ + Q_INVOKABLE explicit AnytoneAPRSFrequency(QObject *parent=nullptr); + + ConfigItem *clone() const; + + /** Returns the transmit frequency. */ + Frequency frequency() const; + /** Sets the transmit frequency. */ + void setFrequency(Frequency freq); + +protected: + /** The transmit frequency. */ + Frequency _frequency; +}; + + +/** Represents a reference to an APRS frequency. + * @ingroup anytone */ +class AnytoneAPRSFrequencyRef: public ConfigObjectReference +{ + Q_OBJECT + +public: + /** Default constructor. */ + explicit AnytoneAPRSFrequencyRef(QObject *parent=nullptr); +}; + + +/** Represents a list of APRS transmit frequencies. + * @ingroup anytone */ +class AnytoneAPRSFrequencyList: public ConfigObjectList +{ + Q_OBJECT + +public: + /** Empty constructor. */ + explicit AnytoneAPRSFrequencyList(QObject *parent=nullptr); + + ConfigItem *allocateChild(const YAML::Node &node, ConfigItem::Context &ctx, const ErrorStack &err); +}; + + /** Implements the common properties for analog and digital AnyTone channels. * This class cannot be instantiated directly, use one of the derived classes. * @ingroup anytone */ @@ -23,6 +79,8 @@ class AnytoneChannelExtension: public ConfigExtension Q_PROPERTY(int frequencyCorrection READ frequencyCorrection WRITE setFrequencyCorrection) /** If @c true, the hands-free featrue is enabled for this channel. */ Q_PROPERTY(bool handsFree READ handsFree WRITE enableHandsFree) + /** A reference to the FM APRS frequency. If not set, the default will be used. */ + Q_PROPERTY(AnytoneAPRSFrequencyRef *fmAPRSFrequency READ fmAPRSFrequency()) protected: /** Hidden constructor. */ @@ -44,6 +102,8 @@ public: /** Enables/disables the hands-free feature for this channel. */ void enableHandsFree(bool enable); + /** Holds a reference to the FM APRS frequency to be used if FM APRS is enabled on the channel. */ + AnytoneAPRSFrequencyRef *fmAPRSFrequency() const; protected: /** If @c true, talkaround is enabled. */ @@ -52,6 +112,8 @@ protected: int _frequencyCorrection; /** If @c true, the hands-free featrue is enabled for this channel. */ bool _handsFree; + /** A reference to the FM APRS frequency. */ + AnytoneAPRSFrequencyRef *_fmAPRSFrequency; }; @@ -2280,6 +2342,7 @@ protected: bool _maintainCallChannel; ///< Maintains the call channel. }; + /** Implements some additional settings for the FM APRS system. * This extension gets attached to a @c APRSSystem instance. */ @@ -2312,6 +2375,9 @@ class AnytoneFMAPRSSettingsExtension: public ConfigExtension /** If @c true, the report other flag is set. */ Q_PROPERTY(bool reportOther READ reportOther WRITE enableReportOther) + /** The list of additional APRS frequencies. */ + Q_PROPERTY(AnytoneAPRSFrequencyList *frequencies READ frequencies) + public: /** Possible bandwidth settings. */ enum class Bandwidth { @@ -2377,6 +2443,9 @@ public: /** Enables/disables report other flag. */ void enableReportOther(bool enable); + /** Returns the list of additional FM APRS frequencies. */ + AnytoneAPRSFrequencyList *frequencies() const; + protected: /** The transmit delay. */ Interval _txDelay; @@ -2402,6 +2471,8 @@ protected: bool _reportStatus; /** The report other flag. */ bool _reportOther; + /** The list of additional FM APRS frequencies. */ + AnytoneAPRSFrequencyList *_frequencies; }; #endif // ANYTONEEXTENSION_HH diff --git a/lib/d578uv_codeplug.hh b/lib/d578uv_codeplug.hh index 2ebddd2c..2eb5e8c4 100644 --- a/lib/d578uv_codeplug.hh +++ b/lib/d578uv_codeplug.hh @@ -102,12 +102,13 @@ class GPSSystem; * * GPS/APRS * Start Size Content - * 02501000 000040 APRS settings, see @c D878UVCodeplug::AnalogAPRSSettingsElement. - * 02501040 000060 APRS settings, see @c D878UVCodeplug::DMRAPRSSystemsElement. - * 025010A0 000060 Extended APRS settings, see @c D578UVCodeplug::AnalogAPRSSettingsExtensionElement. + * 02501000 000100 APRS settings, see @c D878UVCodeplug::APRSSettingsElement. * 02501200 000040 APRS Text, up to 60 chars ASCII, 0-padded. * 02501800 000100 APRS-RX settings list up to 32 entries, 8b each. * See @c D878UVCodeplug::AnalogAPRSRXEntryElement. + * 02502000 000080 FM APRS frequency names, + * see @c FMAPRSFrequencyNamesElement. This element is not part of the manufacturer codeplug. + * QDMR uses this memory section to store additional information. * * General Settings * Start Size Content diff --git a/lib/d868uv_codeplug.hh b/lib/d868uv_codeplug.hh index 97e266b6..01ede738 100644 --- a/lib/d868uv_codeplug.hh +++ b/lib/d868uv_codeplug.hh @@ -280,6 +280,12 @@ public: virtual bool linkChannelObj(Channel *c, Context &ctx) const; /** Initializes this codeplug channel from the given generic configuration. */ virtual bool fromChannelObj(const Channel *c, Context &ctx); + + protected: + /** Internal used offsets within the channel element. */ + struct Offset: public AnytoneCodeplug::ChannelElement::Offset { + /// @todo Implement + }; }; /** Represents the general config of the radio within the D868UV binary codeplug. diff --git a/lib/d878uv_codeplug.cc b/lib/d878uv_codeplug.cc index c764c3ca..2054b5f6 100644 --- a/lib/d878uv_codeplug.cc +++ b/lib/d878uv_codeplug.cc @@ -103,87 +103,97 @@ D878UVCodeplug::ChannelElement::clear() { D878UVCodeplug::ChannelElement::PTTId D878UVCodeplug::ChannelElement::pttIDSetting() const { - return (PTTId)getUInt2(0x0019, 0); + return (PTTId)getUInt2(Offset::pttIDSetting(), 0); } void D878UVCodeplug::ChannelElement::setPTTIDSetting(PTTId ptt) { - setUInt2(0x0019, 0, (unsigned)ptt); + setUInt2(Offset::pttIDSetting(), 0, (unsigned)ptt); } bool D878UVCodeplug::ChannelElement::roamingEnabled() const { // inverted - return !getBit(0x0034, 2); + return !getBit(Offset::roamingEnabled(), 2); } void D878UVCodeplug::ChannelElement::enableRoaming(bool enable) { // inverted - setBit(0x0034, 2, !enable); + setBit(Offset::roamingEnabled(), 2, !enable); } bool D878UVCodeplug::ChannelElement::dataACK() const { // inverted - return !getBit(0x0034, 3); + return !getBit(Offset::dataACK(), 3); } void D878UVCodeplug::ChannelElement::enableDataACK(bool enable) { // inverted - setBit(0x0034, 3, !enable); + setBit(Offset::dataACK(), 3, !enable); } bool D878UVCodeplug::ChannelElement::txDigitalAPRS() const { - return 2 == getUInt2(0x0035, 0); + return 2 == getUInt2(Offset::txDMRAPRS(), 0); } void D878UVCodeplug::ChannelElement::enableTXDigitalAPRS(bool enable) { - setUInt2(0x0035, 0, (enable ? 0x02 : 0x00)); + setUInt2(Offset::txDMRAPRS(), 0, (enable ? 0x02 : 0x00)); } bool D878UVCodeplug::ChannelElement::txAnalogAPRS() const { - return 1 == getUInt2(0x0035, 0); + return 1 == getUInt2(Offset::txDMRAPRS(), 0); } void D878UVCodeplug::ChannelElement::enableTXAnalogAPRS(bool enable) { - setUInt2(0x0035, 0, (enable ? 0x01 : 0x00)); + setUInt2(Offset::txDMRAPRS(), 0, (enable ? 0x01 : 0x00)); } D878UVCodeplug::ChannelElement::APRSPTT D878UVCodeplug::ChannelElement::analogAPRSPTTSetting() const { - return (APRSPTT)getUInt8(0x0036); + return (APRSPTT)getUInt8(Offset::fmAPRSPTTSetting()); } void D878UVCodeplug::ChannelElement::setAnalogAPRSPTTSetting(APRSPTT ptt) { - setUInt8(0x0036, (unsigned)ptt); + setUInt8(Offset::fmAPRSPTTSetting(), (unsigned)ptt); } D878UVCodeplug::ChannelElement::APRSPTT D878UVCodeplug::ChannelElement::digitalAPRSPTTSetting() const { - return (APRSPTT)getUInt8(0x0037); + return (APRSPTT)getUInt8(Offset::dmrAPRSPTTSetting()); } void D878UVCodeplug::ChannelElement::setDigitalAPRSPTTSetting(APRSPTT ptt) { - setUInt8(0x0037, (unsigned)ptt); + setUInt8(Offset::dmrAPRSPTTSetting(), (unsigned)ptt); } unsigned D878UVCodeplug::ChannelElement::digitalAPRSSystemIndex() const { - return getUInt8(0x0038); + return getUInt8(Offset::dmrAPRSSystemIndex()); } void D878UVCodeplug::ChannelElement::setDigitalAPRSSystemIndex(unsigned idx) { - setUInt8(0x0038, idx); + setUInt8(Offset::dmrAPRSSystemIndex(), idx); } int D878UVCodeplug::ChannelElement::frequenyCorrection() const { - return ((int)getInt8(0x0039))*10; + return ((int)getInt8(Offset::frequenyCorrection()))*10; } void D878UVCodeplug::ChannelElement::setFrequencyCorrection(int corr) { - setInt8(0x0039, corr/10); + setInt8(Offset::frequenyCorrection(), corr/10); } +unsigned int +D878UVCodeplug::ChannelElement::fmAPRSFrequencyIndex() const { + return getUInt8(Offset::fmAPRSFrequencyIndex()); +} +void +D878UVCodeplug::ChannelElement::setFMAPRSFrequencyIndex(unsigned int idx) { + setUInt8(Offset::fmAPRSFrequencyIndex(), std::min(7U, idx)); +} + + Channel * D878UVCodeplug::ChannelElement::toChannelObj(Context &ctx) const { Channel *ch = D868UVCodeplug::ChannelElement::toChannelObj(ctx); @@ -224,12 +234,26 @@ D878UVCodeplug::ChannelElement::linkChannelObj(Channel *c, Context &ctx) const { // If roaming is not disabled -> link to default roaming zone if (roamingEnabled()) dc->setRoamingZone(DefaultRoamingZone::get()); + if (auto *ext = dc->anytoneChannelExtension()) { + // If not default FM APRS frequency + if (0 != fmAPRSFrequencyIndex()) { + if (ctx.has(fmAPRSFrequencyIndex())) + ext->fmAPRSFrequency()->set(ctx.get(fmAPRSFrequencyIndex())); + } + } } else if (c->is()) { FMChannel *ac = c->as(); // Link APRS system if one is defined // There can only be one active APRS system, hence the index is fixed to one. if (txAnalogAPRS() && ctx.has(0)) ac->setAPRSSystem(ctx.get(0)); + if (auto *ext = ac->anytoneChannelExtension()) { + // If not default FM APRS frequency + if (0 != fmAPRSFrequencyIndex()) { + if (ctx.has(fmAPRSFrequencyIndex())) + ext->fmAPRSFrequency()->set(ctx.get(fmAPRSFrequencyIndex())); + } + } } return true; @@ -272,6 +296,16 @@ D878UVCodeplug::ChannelElement::fromChannelObj(const Channel *c, Context &ctx) { // Apply extension settings if (AnytoneFMChannelExtension *ext = ac->anytoneChannelExtension()) { setFrequencyCorrection(ext->frequencyCorrection()); + if (! ext->fmAPRSFrequency()->isNull()) { + int idx = ctx.index(ext->fmAPRSFrequency()->as()); + if ((0 <= idx) && (7 >= idx)) + setFMAPRSFrequencyIndex(idx); + else + setFMAPRSFrequencyIndex(0); + } else { + // Use default + setFMAPRSFrequencyIndex(0); + } } } @@ -279,6 +313,39 @@ D878UVCodeplug::ChannelElement::fromChannelObj(const Channel *c, Context &ctx) { } +/* ******************************************************************************************** * + * Implementation of D878UVCodeplug::FMAPRSFrequencyNamesElement + * ******************************************************************************************** */ +D878UVCodeplug::FMAPRSFrequencyNamesElement::FMAPRSFrequencyNamesElement(uint8_t *ptr, size_t size) + : Element(ptr, size) +{ + // pass... +} + +D878UVCodeplug::FMAPRSFrequencyNamesElement::FMAPRSFrequencyNamesElement(uint8_t *ptr) + : Element(ptr, size()) +{ + // pass... +} + +void +D878UVCodeplug::FMAPRSFrequencyNamesElement::clear() { + memset(_data, 0xff, size()); +} + +QString +D878UVCodeplug::FMAPRSFrequencyNamesElement::name(unsigned int n) const { + n = std::min(n, 7U); + return readASCII(n*Offset::betweenNames(), Limit::nameLength(), 0xff); +} + +void +D878UVCodeplug::FMAPRSFrequencyNamesElement::setName(unsigned int n, const QString &name) { + n = std::min(n, 7U); + writeASCII(n*Offset::betweenNames(), name, Limit::nameLength(), 0xff); +} + + /* ******************************************************************************************** * * Implementation of D878UVCodeplug::RoamingChannelElement * ******************************************************************************************** */ @@ -2847,7 +2914,9 @@ D878UVCodeplug::APRSSettingsElement::clearFMFrequency(unsigned int n) { } bool -D878UVCodeplug::APRSSettingsElement::fromFMAPRSSystem(const APRSSystem *sys, Context &ctx, const ErrorStack &err) { +D878UVCodeplug::APRSSettingsElement::fromFMAPRSSystem( + const APRSSystem *sys, Context &ctx, FMAPRSFrequencyNamesElement &names, const ErrorStack &err) +{ Q_UNUSED(ctx) clear(); if (! sys->revertChannel()) { @@ -2855,6 +2924,7 @@ D878UVCodeplug::APRSSettingsElement::fromFMAPRSSystem(const APRSSystem *sys, Con << "No revert channel defined for APRS system '" << sys->name() <<"'."; return false; } + names.setName(0, sys->name()); setFMFrequency(0, Frequency::fromHz(sys->revertChannel()->txFrequency()*1e6)); setTXTone(sys->revertChannel()->txTone()); setPower(sys->revertChannel()->power()); @@ -2878,13 +2948,23 @@ D878UVCodeplug::APRSSettingsElement::fromFMAPRSSystem(const APRSSystem *sys, Con setFMPreWaveDelay(ext->preWaveDelay()); enableFMPassAll(ext->passAll()); + // Encode additional FM APRS frequencies + for (int i=0; ifrequencies()->count(); i++) { + setFMFrequency(ctx.index(ext->frequencies()->get(i)), + ext->frequencies()->get(i)->as()->frequency()); + names.setName(ctx.index(ext->frequencies()->get(i)), + ext->frequencies()->get(i)->name()); + } return true; } APRSSystem * -D878UVCodeplug::APRSSettingsElement::toFMAPRSSystem() { +D878UVCodeplug::APRSSettingsElement::toFMAPRSSystem(Context &ctx, const FMAPRSFrequencyNamesElement &names, const ErrorStack &err) { + QString name = QString("APRS %1").arg(destination()); + if (! names.name(0).isEmpty()) + name = names.name(0); APRSSystem *sys = new APRSSystem( - tr("APRS %1").arg(destination()), nullptr, + name, nullptr, destination(), destinationSSID(), source(), sourceSSID(), path(), icon(), "", autoTXInterval().seconds()); @@ -2896,6 +2976,19 @@ D878UVCodeplug::APRSSettingsElement::toFMAPRSSystem() { ext->setPreWaveDelay(fmPreWaveDelay()); ext->enablePassAll(fmPassAll()); + for (unsigned int i=1; isetFrequency(fmFrequency(i)); + QString name = QString("APRS %1").arg(i); + if (! names.name(i).isEmpty()) + name = names.name(i); + f->setName(name); + ext->frequencies()->add(f); + ctx.add(f, i); + } + return sys; } @@ -3281,6 +3374,9 @@ D878UVCodeplug::allocateUpdated() { // allocate APRS RX list image(0).addElement(Offset::analogAPRSRXEntries(), Limit::analogAPRSRXEntries()*AnalogAPRSRXEntryElement::size()); + + // allocate FM APRS frequency names + image(0).addElement(Offset::fmAPRSFrequencyNames(), FMAPRSFrequencyNamesElement::size()); } void @@ -3295,6 +3391,8 @@ D878UVCodeplug::allocateForDecoding() { // First allocate everything common between D868UV and D878UV codeplugs. D868UVCodeplug::allocateForDecoding(); this->allocateRoaming(); + // allocate FM APRS frequency names + image(0).addElement(Offset::fmAPRSFrequencyNames(), FMAPRSFrequencyNamesElement::size()); } void @@ -3514,9 +3612,11 @@ D878UVCodeplug::encodeGPSSystems(const Flags &flags, Context &ctx, const ErrorSt // replaces D868UVCodeplug::encodeGPSSystems APRSSettingsElement aprs(data(Offset::aprsSettings())); + FMAPRSFrequencyNamesElement aprsNames(data(Offset::fmAPRSFrequencyNames())); + // Encode APRS system (there can only be one) if (0 < ctx.config()->posSystems()->aprsCount()) { - aprs.fromFMAPRSSystem(ctx.config()->posSystems()->aprsSystem(0), ctx); + aprs.fromFMAPRSSystem(ctx.config()->posSystems()->aprsSystem(0), ctx, aprsNames, err); AnalogAPRSMessageElement(data(Offset::analogAPRSMessage())) .setMessage(ctx.config()->posSystems()->aprsSystem(0)->message()); } @@ -3542,12 +3642,17 @@ D878UVCodeplug::createGPSSystems(Context &ctx, const ErrorStack &err) { // Before creating any GPS/APRS systems, get global auto TX interval APRSSettingsElement aprs(data(Offset::aprsSettings())); + FMAPRSFrequencyNamesElement aprsNames(data(Offset::fmAPRSFrequencyNames())); AnalogAPRSMessageElement aprsMessage(data(Offset::analogAPRSMessage())); unsigned pos_intervall = aprs.autoTXInterval().seconds(); // Create APRS system (if enabled) if (aprs.isValid()) { - APRSSystem *sys = aprs.toFMAPRSSystem(); + APRSSystem *sys = aprs.toFMAPRSSystem(ctx, aprsNames, err); + if (nullptr == sys) { + errMsg(err) << "Cannot decode positioning systems."; + return false; + } sys->setPeriod(pos_intervall); sys->setMessage(aprsMessage.message()); ctx.config()->posSystems()->add(sys); ctx.add(sys,0); diff --git a/lib/d878uv_codeplug.hh b/lib/d878uv_codeplug.hh index 6298d814..1e3cfad4 100644 --- a/lib/d878uv_codeplug.hh +++ b/lib/d878uv_codeplug.hh @@ -142,6 +142,9 @@ class GPSSystem; * 02501280 000030 APRS template text (optional settings). * 02501800 000100 APRS-RX settings list up to 32 entries, 8b each. * See @c D878UVCodeplug::AnalogAPRSRXEntryElement. + * 02502000 000080 FM APRS frequency names, + * see @c FMAPRSFrequencyNamesElement. This element is not part of the manufacturer codeplug. + * QDMR uses this memory section to store additional information. * * General Settings * Start Size Content @@ -339,12 +342,33 @@ public: /** Sets the frequency correction in ???. */ virtual void setFrequencyCorrection(int corr); + /** Returns the index of the FM APRS frequency [0,7]. */ + virtual unsigned int fmAPRSFrequencyIndex() const; + /** Sets the FM APRS frequency index [0,7]. */ + virtual void setFMAPRSFrequencyIndex(unsigned int idx); + /** Constructs a Channel object from this element. */ Channel *toChannelObj(Context &ctx) const; /** Links a previously created channel object. */ bool linkChannelObj(Channel *c, Context &ctx) const; /** Encodes the given channel object. */ bool fromChannelObj(const Channel *c, Context &ctx); + + protected: + /** Internal used offsets within the channel element. */ + struct Offset : public D868UVCodeplug::ChannelElement::Offset { + /// @cond DO_NOT_DOCUMENT + static constexpr unsigned int pttIDSetting() { return 0x0019; } + static constexpr unsigned int roamingEnabled() { return 0x0034; } + static constexpr unsigned int dataACK() { return 0x0034; } + static constexpr unsigned int txDMRAPRS() { return 0x0035; } + static constexpr unsigned int fmAPRSPTTSetting() { return 0x0036; } + static constexpr unsigned int dmrAPRSPTTSetting() { return 0x0037; } + static constexpr unsigned int dmrAPRSSystemIndex() { return 0x0038; } + static constexpr unsigned int frequenyCorrection() { return 0x0039; } + static constexpr unsigned int fmAPRSFrequencyIndex() { return 0x003c; } + /// @endcond + }; }; /** Represents the general config of the radio within the D878UV binary codeplug. @@ -1149,6 +1173,45 @@ public: }; }; + + /** Implements some storage to hold the names for the FM APRS frequencies. + * @verbinclude d878uv_fm_aprs_frequency_names.txt */ + class FMAPRSFrequencyNamesElement: public Element + { + protected: + /** Hidden constructor. */ + FMAPRSFrequencyNamesElement(uint8_t *ptr, size_t size); + + public: + /** Constructor. */ + explicit FMAPRSFrequencyNamesElement(uint8_t *ptr); + + /** The size of the element. */ + static constexpr unsigned int size() { return 0x0080; } + + void clear(); + + /** Returns the n-th name. The 0-th name, is the name of the FM APRS system. */ + virtual QString name(unsigned int n) const; + /** Sets the n-th name. The 0-th name, is the name of the FM APRS system. */ + virtual void setName(unsigned int n, const QString &name); + + public: + /** Some limits for the element. */ + 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 betweenNames() { return 0x0010; } + /// @endcond + }; + }; + + /** Represents the APRS settings within the binary D878UV codeplug. * * Memory layout of APRS settings (size 0x00f0 bytes): @@ -1347,9 +1410,11 @@ public: /** Configures this APRS system from the given generic config. */ virtual bool fromFMAPRSSystem(const APRSSystem *sys, Context &ctx, + FMAPRSFrequencyNamesElement &names, const ErrorStack &err=ErrorStack()); /** Constructs a generic APRS system configuration from this APRS system. */ - virtual APRSSystem *toFMAPRSSystem(); + virtual APRSSystem *toFMAPRSSystem( + Context &ctx, const FMAPRSFrequencyNamesElement &names, const ErrorStack &err=ErrorStack()); /** Links the transmit channel within the generic APRS system based on the transmit frequency * defined within this APRS system. */ virtual bool linkFMAPRSSystem(APRSSystem *sys, Context &ctx); @@ -1577,6 +1642,7 @@ public: }; }; + /** Represents the bitmap, indicating which roaming channel is valid. */ class RoamingChannelBitmapElement: public BitmapElement { @@ -1872,6 +1938,7 @@ protected: static constexpr unsigned int aprsSettings() { return 0x02501000; } static constexpr unsigned int analogAPRSMessage() { return 0x02501200; } static constexpr unsigned int analogAPRSRXEntries() { return 0x02501800; } + static constexpr unsigned int fmAPRSFrequencyNames() { return 0x02502000; } static constexpr unsigned int hiddenZoneBitmap() { return 0x024c1360; } static constexpr unsigned int roamingChannelBitmap() { return 0x01042000; } static constexpr unsigned int roamingChannels() { return 0x01040000; } diff --git a/lib/dmr6x2uv_codeplug.cc b/lib/dmr6x2uv_codeplug.cc index 82025ebe..90e60877 100644 --- a/lib/dmr6x2uv_codeplug.cc +++ b/lib/dmr6x2uv_codeplug.cc @@ -2000,12 +2000,16 @@ void DMR6X2UVCodeplug::allocateUpdated() { // First allocate everything common between D868UV and DMR-6X2UV codeplugs. D868UVCodeplug::allocateUpdated(); + + // allocate FM APRS frequency names + image(0).addElement(Offset::fmAPRSFrequencyNames(), D878UVCodeplug::FMAPRSFrequencyNamesElement::size()); } void DMR6X2UVCodeplug::allocateForEncoding() { // First allocate everything common between D868UV and D878UV codeplugs. D868UVCodeplug::allocateForEncoding(); + this->allocateRoaming(); } @@ -2013,7 +2017,11 @@ void DMR6X2UVCodeplug::allocateForDecoding() { // First allocate everything common between D868UV and D878UV codeplugs. D868UVCodeplug::allocateForDecoding(); + this->allocateRoaming(); + + // allocate FM APRS frequency names + image(0).addElement(Offset::fmAPRSFrequencyNames(), D878UVCodeplug::FMAPRSFrequencyNamesElement::size()); } bool @@ -2158,10 +2166,11 @@ DMR6X2UVCodeplug::encodeGPSSystems(const Flags &flags, Context &ctx, const Error // replaces D868UVCodeplug::encodeGPSSystems D878UVCodeplug::APRSSettingsElement aprs(data(Offset::aprsSettings())); + D878UVCodeplug::FMAPRSFrequencyNamesElement aprsNames(data(Offset::fmAPRSFrequencyNames())); // Encode APRS system (there can only be one) if (0 < ctx.config()->posSystems()->aprsCount()) { - aprs.fromFMAPRSSystem(ctx.config()->posSystems()->aprsSystem(0), ctx); + aprs.fromFMAPRSSystem(ctx.config()->posSystems()->aprsSystem(0), ctx, aprsNames); uint8_t *aprsmsg = (uint8_t *)data(Offset::fmAPRSMessage()); encode_ascii(aprsmsg, ctx.config()->posSystems()->aprsSystem(0)->message(), Limit::fmAPRSMessage(), 0x00); } @@ -2186,12 +2195,17 @@ DMR6X2UVCodeplug::createGPSSystems(Context &ctx, const ErrorStack &err) { // Before creating any GPS/APRS systems, get global auto TX interval D878UVCodeplug::APRSSettingsElement aprs(data(Offset::aprsSettings())); + D878UVCodeplug::FMAPRSFrequencyNamesElement aprsNames(data(Offset::fmAPRSFrequencyNames())); unsigned pos_interval = aprs.autoTXInterval().seconds(); // Create APRS system (if enabled) uint8_t *aprsmsg = (uint8_t *)data(Offset::fmAPRSMessage()); if (aprs.isValid()) { - APRSSystem *sys = aprs.toFMAPRSSystem(); + APRSSystem *sys = aprs.toFMAPRSSystem(ctx, aprsNames, err); + if (nullptr == sys) { + errMsg(err) << "Cannot decode positioning systems."; + return false; + } sys->setPeriod(pos_interval); sys->setMessage(decode_ascii(aprsmsg, Limit::fmAPRSMessage(), 0x00)); ctx.config()->posSystems()->add(sys); ctx.add(sys,0); diff --git a/lib/dmr6x2uv_codeplug.hh b/lib/dmr6x2uv_codeplug.hh index 05322dbf..6ae67469 100644 --- a/lib/dmr6x2uv_codeplug.hh +++ b/lib/dmr6x2uv_codeplug.hh @@ -103,6 +103,9 @@ * see @c DMR6X2UVCodeplug::APRSSettingsElement. * 02501200 000040 APRS Text, up to 60 chars ASCII, 0-padded. * 02501280 000030 GPS template message, ASCII, 0-padded. + * 02502000 000080 FM APRS frequency names, + * see @c FMAPRSFrequencyNamesElement. This element is not part of the manufacturer codeplug. + * QDMR uses this memory section to store additional information. * * General Settings * Start Size Content @@ -1155,6 +1158,7 @@ protected: static constexpr unsigned int roamingZones() { return 0x01043000; } static constexpr unsigned int fmAPRSMessage() { return 0x02501200; } + static constexpr unsigned int fmAPRSFrequencyNames() { return 0x02502000; } static constexpr unsigned int settingsExtension() { return 0x02501400; } /// @endcond };