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
{
bool isNumber;
const double frequencyMhz = receiver.toDouble(&isNumber);
if (isNumber)
CFrequency radioFrequency = CComSystem::parseComFrequency(receiver, CPqString::SeparatorsBestGuess);
if (!radioFrequency.isNull())
{
CFrequency radioFrequency = CFrequency(frequencyMhz, CFrequencyUnit::MHz());
CComSystem::roundToChannelSpacing(radioFrequency, CComSystem::ChannelSpacing8_33KHz);
if (CComSystem::isValidCivilAviationFrequency(radioFrequency))
{
tm.setFrequency(radioFrequency);

View File

@@ -8,8 +8,8 @@
*/
#include "blackmisc/aviation/comsystem.h"
#include "blackmisc/dbus.h"
#include "blackmisc/math/mathutils.h"
#include "blackmisc/dbus.h"
#include <QDBusMetaType>
#include <QtDebug>
@@ -102,24 +102,28 @@ namespace BlackMisc
bool CComSystem::isValidCivilAviationFrequency(const CFrequency &f)
{
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)
{
if (f.isNull()) return false;
double fr = f.valueRounded(BlackMisc::PhysicalQuantities::CFrequencyUnit::MHz(), 3);
return fr >= 220.0 && fr <= 399.95;
const int fr = f.valueInteger(PhysicalQuantities::CFrequencyUnit::kHz());
return fr >= 220000 && fr <= 399950;
}
bool CComSystem::isValidComFrequency(const CFrequency &f)
{
if (f.isNull()) { return false; }
return isValidCivilAviationFrequency(f) || isValidMilitaryFrequency(f);
}
void CComSystem::roundToChannelSpacing(CFrequency &frequency, ChannelSpacing channelSpacing)
{
if (frequency.isNull()) { return; }
const double channelSpacingKHz = CComSystem::channelSpacingToFrequencyKHz(channelSpacing);
const double f = frequency.valueRounded(CFrequencyUnit::kHz(), 0);
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)
{
if (setFrequency.isNull()) { return false; }
if (setFrequency == compareFrequency) return true; // shortcut for many of such comparisons
double channelSpacingKHz = 0.5 * CComSystem::channelSpacingToFrequencyKHz(channelSpacing);
double compareFrequencyKHz = compareFrequency.value(CFrequencyUnit::kHz());
@@ -141,6 +146,38 @@ namespace BlackMisc
(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)
{
switch (channelSpacing)

View File

@@ -13,15 +13,16 @@
#define BLACKMISC_AVIATION_COMSYSTEM_H
#include "blackmisc/aviation/modulator.h"
#include "blackmisc/blackmiscexport.h"
#include "blackmisc/dictionary.h"
#include "blackmisc/json.h"
#include "blackmisc/metaclass.h"
#include "blackmisc/pq/constants.h"
#include "blackmisc/pq/frequency.h"
#include "blackmisc/pq/physicalquantity.h"
#include "blackmisc/pq/units.h"
#include "blackmisc/pq/pqstring.h"
#include "blackmisc/blackmiscexport.h"
#include "blackmisc/propertyindexvariantmap.h"
#include "blackmisc/dictionary.h"
#include "blackmisc/json.h"
#include "blackmisc/variant.h"
#include <QHash>
@@ -132,6 +133,9 @@ namespace BlackMisc
const PhysicalQuantities::CFrequency &compareFrequency,
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
static void registerMetadata();

View File

@@ -354,26 +354,93 @@ namespace BlackMisc
*/
template <class U> static U unitFromSymbol(const QString &symbol, bool strict = true)
{
if (symbol.isEmpty()) return U::defaultUnit();
for (const auto unit : U::allUnits())
if (symbol.isEmpty()) { return U::defaultUnit(); }
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");
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?
* \param symbol to be tested
* \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;
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;
}

View File

@@ -58,9 +58,10 @@ namespace BlackMisc
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);
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 ""; }
return m_originalLine.mid(fi).trimmed();
}

View File

@@ -395,6 +395,22 @@ namespace BlackMisc
if (question.endsWith("?")) { return question; }
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
//! \endcond

View File

@@ -59,6 +59,9 @@ namespace BlackMisc
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.
//! \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)