Ref T338, utility functions / parsing

- nthIndexOf
- measurement unit can be parsed case insensitive
- CSimpleCommandParser::remainingStringAfter from n-th space
- improved frequency parsing
This commit is contained in:
Klaus Basan
2018-09-08 02:11:03 +02:00
parent 3d2d999f7a
commit a9d728f451
7 changed files with 146 additions and 21 deletions

View File

@@ -322,12 +322,9 @@ namespace BlackCore
} }
else else
{ {
bool isNumber; CFrequency radioFrequency = CComSystem::parseComFrequency(receiver, CPqString::SeparatorsBestGuess);
const double frequencyMhz = receiver.toDouble(&isNumber); if (!radioFrequency.isNull())
if (isNumber)
{ {
CFrequency radioFrequency = CFrequency(frequencyMhz, CFrequencyUnit::MHz());
CComSystem::roundToChannelSpacing(radioFrequency, CComSystem::ChannelSpacing8_33KHz);
if (CComSystem::isValidCivilAviationFrequency(radioFrequency)) if (CComSystem::isValidCivilAviationFrequency(radioFrequency))
{ {
tm.setFrequency(radioFrequency); tm.setFrequency(radioFrequency);

View File

@@ -8,8 +8,8 @@
*/ */
#include "blackmisc/aviation/comsystem.h" #include "blackmisc/aviation/comsystem.h"
#include "blackmisc/dbus.h"
#include "blackmisc/math/mathutils.h" #include "blackmisc/math/mathutils.h"
#include "blackmisc/dbus.h"
#include <QDBusMetaType> #include <QDBusMetaType>
#include <QtDebug> #include <QtDebug>
@@ -102,24 +102,28 @@ namespace BlackMisc
bool CComSystem::isValidCivilAviationFrequency(const CFrequency &f) bool CComSystem::isValidCivilAviationFrequency(const CFrequency &f)
{ {
if (f.isNull()) return false; if (f.isNull()) return false;
double fr = f.valueRounded(BlackMisc::PhysicalQuantities::CFrequencyUnit::MHz(), 3);
return fr >= 118.0 && fr <= 136.975; // comparsion in int avoids double compare issues
const int fr = f.valueInteger(PhysicalQuantities::CFrequencyUnit::kHz());
return fr >= 118000 && fr <= 136975;
} }
bool CComSystem::isValidMilitaryFrequency(const CFrequency &f) bool CComSystem::isValidMilitaryFrequency(const CFrequency &f)
{ {
if (f.isNull()) return false; if (f.isNull()) return false;
double fr = f.valueRounded(BlackMisc::PhysicalQuantities::CFrequencyUnit::MHz(), 3); const int fr = f.valueInteger(PhysicalQuantities::CFrequencyUnit::kHz());
return fr >= 220.0 && fr <= 399.95; return fr >= 220000 && fr <= 399950;
} }
bool CComSystem::isValidComFrequency(const CFrequency &f) bool CComSystem::isValidComFrequency(const CFrequency &f)
{ {
if (f.isNull()) { return false; }
return isValidCivilAviationFrequency(f) || isValidMilitaryFrequency(f); return isValidCivilAviationFrequency(f) || isValidMilitaryFrequency(f);
} }
void CComSystem::roundToChannelSpacing(CFrequency &frequency, ChannelSpacing channelSpacing) void CComSystem::roundToChannelSpacing(CFrequency &frequency, ChannelSpacing channelSpacing)
{ {
if (frequency.isNull()) { return; }
const double channelSpacingKHz = CComSystem::channelSpacingToFrequencyKHz(channelSpacing); const double channelSpacingKHz = CComSystem::channelSpacingToFrequencyKHz(channelSpacing);
const double f = frequency.valueRounded(CFrequencyUnit::kHz(), 0); const double f = frequency.valueRounded(CFrequencyUnit::kHz(), 0);
const quint32 d = static_cast<quint32>(f / channelSpacingKHz); const quint32 d = static_cast<quint32>(f / channelSpacingKHz);
@@ -133,6 +137,7 @@ namespace BlackMisc
bool CComSystem::isWithinChannelSpacing(const CFrequency &setFrequency, const CFrequency &compareFrequency, CComSystem::ChannelSpacing channelSpacing) bool CComSystem::isWithinChannelSpacing(const CFrequency &setFrequency, const CFrequency &compareFrequency, CComSystem::ChannelSpacing channelSpacing)
{ {
if (setFrequency.isNull()) { return false; }
if (setFrequency == compareFrequency) return true; // shortcut for many of such comparisons if (setFrequency == compareFrequency) return true; // shortcut for many of such comparisons
double channelSpacingKHz = 0.5 * CComSystem::channelSpacingToFrequencyKHz(channelSpacing); double channelSpacingKHz = 0.5 * CComSystem::channelSpacingToFrequencyKHz(channelSpacing);
double compareFrequencyKHz = compareFrequency.value(CFrequencyUnit::kHz()); double compareFrequencyKHz = compareFrequency.value(CFrequencyUnit::kHz());
@@ -141,6 +146,38 @@ namespace BlackMisc
(setFrequencyKHz + channelSpacingKHz > compareFrequencyKHz); (setFrequencyKHz + channelSpacingKHz > compareFrequencyKHz);
} }
CFrequency CComSystem::parseComFrequency(const QString &input, CPqString::SeparatorMode sep)
{
if (input.isEmpty()) { return CFrequency::null(); }
CFrequency comFreq;
if (isDigitsOnlyString(input))
{
const double f = input.toDouble();
comFreq = CFrequency(f, f > 999 ? CFrequencyUnit::kHz() : CFrequencyUnit::MHz());
}
else
{
comFreq.parseFromString(input, sep);
if (comFreq.isNull())
{
bool ok;
const double f = CPqString::parseNumber(input, ok, sep);
if (ok)
{
comFreq = CFrequency(f, f > 999 ? CFrequencyUnit::kHz() : CFrequencyUnit::MHz());
}
else
{
comFreq = CFrequency::null();
}
}
}
if (comFreq.isNull()) { return CFrequency::null(); }
roundToChannelSpacing(comFreq, ChannelSpacing8_33KHz);
return isValidComFrequency(comFreq) ? comFreq : CFrequency::null();
}
double CComSystem::channelSpacingToFrequencyKHz(ChannelSpacing channelSpacing) double CComSystem::channelSpacingToFrequencyKHz(ChannelSpacing channelSpacing)
{ {
switch (channelSpacing) switch (channelSpacing)

View File

@@ -13,15 +13,16 @@
#define BLACKMISC_AVIATION_COMSYSTEM_H #define BLACKMISC_AVIATION_COMSYSTEM_H
#include "blackmisc/aviation/modulator.h" #include "blackmisc/aviation/modulator.h"
#include "blackmisc/blackmiscexport.h"
#include "blackmisc/dictionary.h"
#include "blackmisc/json.h"
#include "blackmisc/metaclass.h" #include "blackmisc/metaclass.h"
#include "blackmisc/pq/constants.h" #include "blackmisc/pq/constants.h"
#include "blackmisc/pq/frequency.h" #include "blackmisc/pq/frequency.h"
#include "blackmisc/pq/physicalquantity.h" #include "blackmisc/pq/physicalquantity.h"
#include "blackmisc/pq/units.h" #include "blackmisc/pq/units.h"
#include "blackmisc/pq/pqstring.h"
#include "blackmisc/blackmiscexport.h"
#include "blackmisc/propertyindexvariantmap.h" #include "blackmisc/propertyindexvariantmap.h"
#include "blackmisc/dictionary.h"
#include "blackmisc/json.h"
#include "blackmisc/variant.h" #include "blackmisc/variant.h"
#include <QHash> #include <QHash>
@@ -132,6 +133,9 @@ namespace BlackMisc
const PhysicalQuantities::CFrequency &compareFrequency, const PhysicalQuantities::CFrequency &compareFrequency,
ChannelSpacing channelSpacing); ChannelSpacing channelSpacing);
//! Parses almost any shitty string to a valid COM frequency
static PhysicalQuantities::CFrequency parseComFrequency(const QString &input, PhysicalQuantities::CPqString::SeparatorMode sep);
//! \copydoc BlackMisc::CValueObject::registerMetadata //! \copydoc BlackMisc::CValueObject::registerMetadata
static void registerMetadata(); static void registerMetadata();

View File

@@ -354,26 +354,93 @@ namespace BlackMisc
*/ */
template <class U> static U unitFromSymbol(const QString &symbol, bool strict = true) template <class U> static U unitFromSymbol(const QString &symbol, bool strict = true)
{ {
if (symbol.isEmpty()) return U::defaultUnit(); if (symbol.isEmpty()) { return U::defaultUnit(); }
for (const auto unit : U::allUnits())
static const bool cs = hasCaseSensitiveSymbols<U>();
for (const auto &unit : U::allUnits())
{ {
if (unit.getSymbol() == symbol) { return unit; } if (strict && cs)
{
if (unit.getSymbol() == symbol) { return unit; }
}
else
{
if (stringCompare(unit.getSymbol(), symbol, Qt::CaseInsensitive)) { return unit; }
}
} }
if (strict) qFatal("Illegal unit name"); if (strict) qFatal("Illegal unit name");
return U::defaultUnit(); return U::defaultUnit();
} }
/**
* All symbols
*/
template <class U> static const QStringList &allSymbols()
{
static const QStringList symbols = []
{
QStringList s;
for (const auto &unit : U::allUnits())
{
s.push_back(unit.getSymbol());
}
return s;
}();
return symbols;
}
/**
* All symbols case insensitive
*/
template <class U> static const QStringList &allSymbolsLowerCase()
{
static const QStringList symbols = []
{
QSet<QString> s;
for (const QString &symbol : allSymbols<U>())
{
s.insert(symbol.toLower());
}
return s.toList();
}();
return symbols;
}
/**
* Are symbols case sensitive?
*/
template <class U> static bool hasCaseSensitiveSymbols()
{
static const bool cs = []
{
return (allSymbolsLowerCase<U>().size() != allSymbols<U>().size());
}();
return cs;
}
/*!
* Valid unit symbol?
* \param symbol to be tested
*/
template <class U> static bool isValidUnitSymbol(const QString &symbol)
{
static const bool cs = hasCaseSensitiveSymbols<U>();
return cs ?
isValidUnitSymbol<U>(symbol, Qt::CaseSensitive) :
isValidUnitSymbol<U>(symbol, Qt::CaseInsensitive);
}
/*! /*!
* Valid unit symbol? * Valid unit symbol?
* \param symbol to be tested * \param symbol to be tested
* \param caseSensitivity check case sensitiv? * \param caseSensitivity check case sensitiv?
*/ */
template <class U> static bool isValidUnitSymbol(const QString &symbol, Qt::CaseSensitivity caseSensitivity = Qt::CaseSensitive) template <class U> static bool isValidUnitSymbol(const QString &symbol, Qt::CaseSensitivity caseSensitivity)
{ {
if (symbol.isEmpty()) return false; if (symbol.isEmpty()) return false;
for (const auto unit : U::allUnits()) for (const auto &unit : U::allUnits())
{ {
if (QString::compare(unit.getSymbol(), symbol, caseSensitivity) == 0) { return true; } if (stringCompare(unit.getSymbol(), symbol, caseSensitivity)) { return true; }
} }
return false; return false;
} }

View File

@@ -58,9 +58,10 @@ namespace BlackMisc
QString CSimpleCommandParser::remainingStringAfter(int index) const QString CSimpleCommandParser::remainingStringAfter(int index) const
{ {
if (index < 0) { return m_originalLine.trimmed(); } if (index < 0) { return m_cleanedLine; }
const QString p = this->part(index); const QString p = this->part(index);
int fi = m_originalLine.indexOf(p, 0, Qt::CaseInsensitive); const int from = index < 1 ? 0 : nthIndexOf(m_cleanedLine, ' ', index, Qt::CaseInsensitive);
const int fi = m_cleanedLine.indexOf(p, from, Qt::CaseInsensitive);
if (fi < 0) { return ""; } if (fi < 0) { return ""; }
return m_originalLine.mid(fi).trimmed(); return m_originalLine.mid(fi).trimmed();
} }

View File

@@ -395,6 +395,22 @@ namespace BlackMisc
if (question.endsWith("?")) { return question; } if (question.endsWith("?")) { return question; }
return question % QStringLiteral("?"); return question % QStringLiteral("?");
} }
int nthIndexOf(const QString &string, QChar ch, int nth, Qt::CaseSensitivity cs)
{
if (nth < 1 || string.isEmpty() || nth > string.length()) { return -1; }
int from = 0;
int ci = -1;
for (int t = 0; t < nth; ++t)
{
ci = string.indexOf(ch, from, cs);
if (ci < 0) { return -1; }
from = ci + 1;
if (from >= string.length()) { return -1; }
}
return ci;
}
} // ns } // ns
//! \endcond //! \endcond

View File

@@ -59,6 +59,9 @@ namespace BlackMisc
return static_cast<int>(std::distance(s.begin(), it)); return static_cast<int>(std::distance(s.begin(), it));
} }
//! nth index of ch
BLACKMISC_EXPORT int nthIndexOf(const QString &string, QChar ch, int nth = 1, Qt::CaseSensitivity cs = Qt::CaseInsensitive);
//! Split a string into multiple strings, using a predicate function to identify the split points. //! Split a string into multiple strings, using a predicate function to identify the split points.
//! \warning The returned refs are only valid during the lifetime of the original string. //! \warning The returned refs are only valid during the lifetime of the original string.
template <class F> QList<QStringRef> splitStringRefs(const QString &s, F predicate) template <class F> QList<QStringRef> splitStringRefs(const QString &s, F predicate)