/** * @file Radio.cpp * @brief callsign validation */ #include "Radio.h" #include #include #include #include #include namespace Radio { namespace { double constexpr MHz_factor{1.e6}; int constexpr frequency_precsion{6}; // very loose validation - callsign must contain a letter next to // a number QRegularExpression valid_callsign_regexp{R"(\d[[:alpha:]]|[[:alpha:]]\d)"}; // suffixes that are often used and should not be interpreted as a // DXCC Entity prefix used as a suffix QRegularExpression non_prefix_suffix{ R"(\A([0-9AMPQR]|QRP|F[DF]|[AM]M|L[HT]|LGT)\z)"}; } // namespace Frequency frequency(QVariant const &v, int scale, bool *ok, QLocale const &locale) { double value{0.}; if (QMetaType::QString == v.typeId()) { value = locale.toDouble(v.value(), ok); } else { value = v.toDouble(); if (ok) *ok = true; } value *= std::pow(10., scale); if (ok) { if (value < 0. || value >= static_cast(std::numeric_limits::max())) { value = 0.; *ok = false; } } return std::llround(value); } FrequencyDelta frequency_delta(QVariant const &v, int scale, bool *ok, QLocale const &locale) { double value{0.}; if (QMetaType::QString == v.typeId()) { value = locale.toDouble(v.value(), ok); } else { value = v.toDouble(); if (ok) *ok = true; } value *= std::pow(10., scale); if (ok) { auto const limit = static_cast(std::numeric_limits::max()); if (value <= -limit || value >= limit) { value = 0.; *ok = false; } } return std::llround(value); } QString frequency_MHz_string(Frequency f, QLocale const &locale) { return locale.toString(f / MHz_factor, 'f', frequency_precsion); } QString frequency_MHz_string(FrequencyDelta d, QLocale const &locale) { return locale.toString(d / MHz_factor, 'f', frequency_precsion); } QString pretty_frequency_MHz_string(Frequency f, QLocale const &locale) { auto f_string = locale.toString(f / MHz_factor, 'f', frequency_precsion); return f_string.insert(f_string.size() - 3, QChar::Nbsp); } QString pretty_frequency_MHz_string(double f, int scale, QLocale const &locale) { auto f_string = locale.toString(f / std::pow(10., scale - 6), 'f', frequency_precsion); return f_string.insert(f_string.size() - 3, QChar::Nbsp); } QString pretty_frequency_MHz_string(FrequencyDelta d, QLocale const &locale) { auto d_string = locale.toString(d / MHz_factor, 'f', frequency_precsion); return d_string.insert(d_string.size() - 3, QChar::Nbsp); } bool is_callsign(QString const &callsign) { return callsign.contains(valid_callsign_regexp); } bool is_compound_callsign(QString const &callsign) { return callsign.contains('/'); } // split on first '/' and return the larger portion or the whole if // there is no '/' QString base_callsign(QString callsign) { auto slash_pos = callsign.indexOf('/'); if (slash_pos >= 0) { auto right_size = callsign.size() - slash_pos - 1; if (right_size >= slash_pos) { callsign = callsign.mid(slash_pos + 1); } else { callsign = callsign.left(slash_pos); } } return callsign.toUpper(); } // analyze the callsign and determine the effective prefix, returns // the full call if no valid prefix (or prefix as a suffix) is specified QString effective_prefix(QString callsign) { auto prefix = callsign.toUpper(); auto slash_pos = callsign.indexOf('/'); if (slash_pos >= 0) { auto right_size = callsign.size() - slash_pos - 1; if (right_size >= slash_pos) // naive call is longer than // prefix/suffix algorithm { prefix = callsign.left(slash_pos); } else { prefix = callsign.mid(slash_pos + 1); if (prefix.contains(non_prefix_suffix)) { prefix = callsign.left(slash_pos); // ignore // non-prefix // suffixes } } } return prefix; } } // namespace Radio