diff --git a/src/blackmisc/avallclasses.h b/src/blackmisc/avallclasses.h index 9bb13e0d6..2f6566cdb 100644 --- a/src/blackmisc/avallclasses.h +++ b/src/blackmisc/avallclasses.h @@ -20,5 +20,6 @@ #include "blackmisc/avaircraft.h" #include "blackmisc/avaircraftlist.h" #include "blackmisc/avinformationmessage.h" +#include "blackmisc/avselcal.h" #endif // guard diff --git a/src/blackmisc/avselcal.cpp b/src/blackmisc/avselcal.cpp new file mode 100644 index 000000000..b01fe4998 --- /dev/null +++ b/src/blackmisc/avselcal.cpp @@ -0,0 +1,209 @@ +#include "avselcal.h" + +using namespace BlackMisc::PhysicalQuantities; + +namespace BlackMisc +{ + namespace Aviation + { + + QList CSelcal::frequencyEquivalents = QList(); + QStringList CSelcal::allCodePairs = QStringList(); + + /* + * Convert to string + */ + QString CSelcal::convertToQString(bool /** i18n **/) const + { + return this->m_code; + } + + /* + * Marshall to DBus + */ + void CSelcal::marshallToDbus(QDBusArgument &argument) const + { + argument << this->m_code; + } + + /* + * Unmarshall from DBus + */ + void CSelcal::unmarshallFromDbus(const QDBusArgument &argument) + { + argument >> this->m_code; + } + + /* + * Equals code? + */ + bool CSelcal::equalsString(const QString &code) const + { + if (code.isEmpty()) return false; + return (this->m_code.compare(code, Qt::CaseInsensitive) == 0); + } + + /* + * SELCAL code frequencies + */ + QList CSelcal::getFrequencies() const + { + QList f; + if (!CSelcal::isValidCode(this->m_code)) return f; + for (int pos = 0; pos < this->m_code.length(); pos++) + { + f.append(CSelcal::audioFrequencyEquivalent(this->m_code.at(pos))); + } + return f; + } + + /* + * Valid characters + */ + const QString &CSelcal::validCharacters() + { + static const QString valid = "ABCDEFGHJKLMPQQRS"; + return valid; + } + + /* + * Valid character? + */ + bool CSelcal::isValidCharacter(QChar c) + { + return CSelcal::validCharacters().contains(c); + } + + /* + * Valid code + */ + bool CSelcal::isValidCode(const QString &code) + { + if (code.length() != 4) return true; + int p1, p2, p3, p4; + if ((p1 = CSelcal::validCharacters().indexOf(code.at(0))) < 0) return false; + if ((p2 = CSelcal::validCharacters().indexOf(code.at(1))) < 0) return false; + if ((p3 = CSelcal::validCharacters().indexOf(code.at(2))) < 0) return false; + if ((p4 = CSelcal::validCharacters().indexOf(code.at(3))) < 0) return false; + if (p1 >= p2 || p3 >= p4) return false; // pair in alphabetical order + if (p1 == p3 || p2 == p3 || p2 == p4 || p3 == p4) return false; // given letter can be used only once in a SELCAL code + return true; + } + + /* + * Character to frequency equivalent + */ + const PhysicalQuantities::CFrequency &CSelcal::audioFrequencyEquivalent(QChar c) + { + if (CSelcal::frequencyEquivalents.isEmpty()) + { + QList frequencies; + frequencies + << CFrequency(312.7, CFrequencyUnit::Hz()) + << CFrequency(346.7, CFrequencyUnit::Hz()) + << CFrequency(384.6, CFrequencyUnit::Hz()) + << CFrequency(426.6, CFrequencyUnit::Hz()) + << CFrequency(473.2, CFrequencyUnit::Hz()) + << CFrequency(524.8, CFrequencyUnit::Hz()) + << CFrequency(582.1, CFrequencyUnit::Hz()) + << CFrequency(645.7, CFrequencyUnit::Hz()) + << CFrequency(716.1, CFrequencyUnit::Hz()) + << CFrequency(794.3, CFrequencyUnit::Hz()) + << CFrequency(881.0, CFrequencyUnit::Hz()) + << CFrequency(977.2, CFrequencyUnit::Hz()) + << CFrequency(1083.9, CFrequencyUnit::Hz()) + << CFrequency(1202.3, CFrequencyUnit::Hz()) + << CFrequency(1333.5, CFrequencyUnit::Hz()) + << CFrequency(1479.1, CFrequencyUnit::Hz()); + CSelcal::frequencyEquivalents = frequencies; + } + int pos = CSelcal::validCharacters().indexOf(c); + Q_ASSERT(pos >= 0); + Q_ASSERT(CSelcal::frequencyEquivalents.size() > pos); + return CSelcal::frequencyEquivalents.at(pos); + } + + /* + * Code pairs + */ + const QStringList &CSelcal::codePairs() + { + if (CSelcal::allCodePairs.isEmpty()) + { + QStringList pairs; + for (int p1 = 0; p1 < (CSelcal::validCharacters().length() - 1); p1++) + { + for (int p2 = p1 + 1; p2 < CSelcal::validCharacters().length(); p2++) + { + QString pair; + pair.append(CSelcal::validCharacters().at(p1)).append(CSelcal::validCharacters().at(p2)); + pairs.append(pair); + } + } + CSelcal::allCodePairs = pairs; + } + return CSelcal::allCodePairs; + } + + /* + * Equal? + */ + bool CSelcal::operator ==(const CSelcal &other) const + { + if (this == &other) return true; + return (this->m_code.compare(other.m_code, Qt::CaseInsensitive) == 0); + } + + /* + * Unequal? + */ + bool CSelcal::operator !=(const CSelcal &other) const + { + return !((*this) == other); + } + + /* + * Hash + */ + uint CSelcal::getValueHash() const + { + return qHash(this->m_code); + } + + /* + * metaTypeId + */ + int CSelcal::getMetaTypeId() const + { + return qMetaTypeId(); + } + + /* + * is a + */ + bool CSelcal::isA(int metaTypeId) const + { + if (metaTypeId == qMetaTypeId()) { return true; } + return this->CValueObject::isA(metaTypeId); + } + + /* + * Compare + */ + int CSelcal::compareImpl(const CValueObject &otherBase) const + { + const auto &other = static_cast(otherBase); + return this->m_code.compare(other.getCode(), Qt::CaseInsensitive); + } + + /* + * Register metadata + */ + void CSelcal::registerMetadata() + { + qRegisterMetaType(); + qDBusRegisterMetaType(); + } + + } // namespace +} // namespace diff --git a/src/blackmisc/avselcal.h b/src/blackmisc/avselcal.h new file mode 100644 index 000000000..78b5a5934 --- /dev/null +++ b/src/blackmisc/avselcal.h @@ -0,0 +1,160 @@ +/* Copyright (C) 2013 VATSIM Community / authors + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/*! + \file +*/ + +#ifndef BLACKMISC_SELCAL_H +#define BLACKMISC_SELCAL_H +#include "blackmisc/pqfrequency.h" +#include "valueobject.h" + +namespace BlackMisc +{ + namespace Aviation + { + /*! + * Value object for SELCAL. + * \see http://en.wikipedia.org/wiki/SELCAL + * \see http://www.asri.aero/our-services/selcal/ User Guide + */ + class CSelcal : public BlackMisc::CValueObject + { + + public: + /*! + * Default constructor. + */ + CSelcal() {} + + /*! + * Constructor. + */ + CSelcal(const QString &code) : m_code(code.trimmed()) {} + + /*! + * Constructor. + * Needed to disambiguate implicit conversion from string literal. + */ + CSelcal(const char *code) : m_code(code) {} + + /*! + * \copydoc CValueObject::toQVariant + */ + virtual QVariant toQVariant() const + { + return QVariant::fromValue(*this); + } + + /*! + * \brief Is valid? + * \return + */ + bool isValid() const { return CSelcal::isValidCode(this->m_code); } + + /*! + * Get SELCAL code + */ + const QString &getCode() const { return this->m_code; } + + /*! + * \brief List of 4 frequencies, if list is empty SELCAL code is not valid + * \return either 4 frequencies, or empty list + */ + QList getFrequencies() const; + + /*! + * \brief Equal operator == + */ + bool operator ==(const CSelcal &other) const; + + /*! + * \brief Unequal operator != + */ + bool operator !=(const CSelcal &other) const; + + /*! + * \copydoc CValueObject::getValueHash + */ + virtual uint getValueHash() const; + + /*! + * \brief Register metadata + */ + static void registerMetadata(); + + /*! + * \brief Equals given string + */ + bool equalsString(const QString &code) const; + + /*! + * \brief Valid SELCAL characters + */ + static const QString &validCharacters(); + + /*! + * \brief Is given character a valid SELCAL characer? + */ + static bool isValidCharacter(QChar c); + + /*! + * Valid SELCAL code? + */ + static bool isValidCode(const QString &code); + + /*! + * \brief Audio frequency for character + */ + static const BlackMisc::PhysicalQuantities::CFrequency &audioFrequencyEquivalent(QChar c); + + /*! + * \brief All valid code pairs: AB, AC, AD ... + */ + static const QStringList &codePairs(); + + protected: + /*! + * \copydoc CValueObject::convertToQString + */ + virtual QString convertToQString(bool i18n = false) const; + + /*! + * \copydoc CValueObject::getMetaTypeId + */ + virtual int getMetaTypeId() const; + + /*! + * \copydoc CValueObject::isA + */ + virtual bool isA(int metaTypeId) const; + + /*! + * \copydoc CValueObject::compareImpl + */ + virtual int compareImpl(const CValueObject &other) const; + + /*! + * \copydoc CValueObject::marshallToDbus + */ + virtual void marshallToDbus(QDBusArgument &argument) const; + + /*! + * \copydoc CValueObject::unmarshallFromDbus + */ + virtual void unmarshallFromDbus(const QDBusArgument &argument); + + private: + QString m_code; + static QList frequencyEquivalents; + static QStringList allCodePairs; + }; + } // namespace +} // namespace + +Q_DECLARE_METATYPE(BlackMisc::Aviation::CSelcal) + +#endif // guard diff --git a/src/blackmisc/blackmiscfreefunctions.cpp b/src/blackmisc/blackmiscfreefunctions.cpp index 514e13882..0e01e9475 100644 --- a/src/blackmisc/blackmiscfreefunctions.cpp +++ b/src/blackmisc/blackmiscfreefunctions.cpp @@ -52,6 +52,7 @@ void BlackMisc::Aviation::registerMetadata() CAircraftList::registerMetadata(); CAircraftSituation::registerMetadata(); CAircraftIcao::registerMetadata(); + CSelcal::registerMetadata(); } /* diff --git a/src/blacksound/soundgenerator.h b/src/blacksound/soundgenerator.h index 04e123524..6f281598e 100644 --- a/src/blacksound/soundgenerator.h +++ b/src/blacksound/soundgenerator.h @@ -6,6 +6,7 @@ #ifndef BLACKSOUND_SOUNDGENERATOR_H #define BLACKSOUND_SOUNDGENERATOR_H +#include "blackmisc/avselcal.h" #include #include #include @@ -118,6 +119,15 @@ namespace BlackSound */ static void playSignal(qint32 volume, const QList &tones, QAudioDeviceInfo device = QAudioDeviceInfo::defaultOutputDevice()); + /*! + * \brief Play SELCAL tone + * \param volume 0-100 + * \param selcal + * \param device device to be used + * \see BlackMisc::Aviation::CSelcal + */ + static void playSelcal(qint32 volume, const BlackMisc::Aviation::CSelcal &selcal, QAudioDeviceInfo device = QAudioDeviceInfo::defaultOutputDevice()); + signals: /*! * \brief Device was closed