swift
comsystem.cpp
1 // SPDX-FileCopyrightText: Copyright (C) 2013 swift Project Community / Contributors
2 // SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-swift-pilot-client-1
3 
5 
6 #include <QDBusMetaType>
7 #include <QtGlobal>
8 
9 #include "misc/math/mathutils.h"
10 #include "misc/pq/literals.h"
11 #include "misc/stringutils.h"
12 
13 using namespace swift::misc::physical_quantities;
14 using namespace swift::misc::math;
15 
16 namespace swift::misc::aviation
17 {
18  void CComSystem::registerMetadata()
19  {
21  qDBusRegisterMetaType<ChannelSpacing>();
22  qDBusRegisterMetaType<ComUnit>();
23  }
24 
25  void CComSystem::setFrequencyActiveMHz(double frequencyMHz)
26  {
27  const CFrequency f(frequencyMHz, CFrequencyUnit::MHz());
28  this->setFrequencyActive(f);
29  }
30 
31  void CComSystem::setFrequencyStandbyMHz(double frequencyMHz)
32  {
33  const CFrequency f(frequencyMHz, CFrequencyUnit::MHz());
34  this->setFrequencyStandby(f);
35  }
36 
37  void CComSystem::setFrequencyActive(const CFrequency &frequency)
38  {
39  if (frequency == this->getFrequencyActive()) { return; } // save all the comparisons / rounding
40  CFrequency fRounded(frequency);
41  roundToChannelSpacing(fRounded, m_channelSpacing);
42  this->CModulator::setFrequencyActive(fRounded);
43  }
44 
45  void CComSystem::setFrequencyStandby(const CFrequency &frequency)
46  {
47  if (frequency == this->getFrequencyStandby()) { return; } // save all the comparisons / rounding
48  CFrequency fRounded(frequency);
49  roundToChannelSpacing(fRounded, m_channelSpacing);
50  this->CModulator::setFrequencyStandby(fRounded);
51  }
52 
53  bool CComSystem::isActiveFrequencySameFrequency(const CFrequency &comFrequency) const
54  {
55  return isSameFrequency(this->getFrequencyActive(), comFrequency);
56  }
57 
58  void CComSystem::setActiveUnicom()
59  {
60  this->toggleActiveStandby();
62  }
63 
64  CComSystem CComSystem::getCom1System(double activeFrequencyMHz, double standbyFrequencyMHz)
65  {
66  return { CModulator::NameCom1(),
68  physical_quantities::CFrequency(standbyFrequencyMHz < 0 ? activeFrequencyMHz : standbyFrequencyMHz,
70  }
71 
72  CComSystem CComSystem::getCom1System(const CFrequency &activeFrequency, const CFrequency &standbyFrequency)
73  {
74  return { CModulator::NameCom1(), activeFrequency,
75  standbyFrequency.isNull() ? activeFrequency : standbyFrequency };
76  }
77 
78  CComSystem CComSystem::getCom2System(double activeFrequencyMHz, double standbyFrequencyMHz)
79  {
80  return { CModulator::NameCom2(),
82  physical_quantities::CFrequency(standbyFrequencyMHz < 0 ? activeFrequencyMHz : standbyFrequencyMHz,
84  }
85 
86  CComSystem CComSystem::getCom2System(const CFrequency &activeFrequency, const CFrequency &standbyFrequency)
87  {
88  return { CModulator::NameCom2(), activeFrequency,
89  standbyFrequency.isNull() ? activeFrequency : standbyFrequency };
90  }
91 
92  bool CComSystem::isValidCivilAviationFrequency(const CFrequency &f)
93  {
94  if (f.isNull()) return false;
95 
96  // comparsion in int avoids double compare issues
98  return fr >= 118000 && fr <= 136990;
99  }
100 
101  bool CComSystem::isValidMilitaryFrequency(const CFrequency &f)
102  {
103  if (f.isNull()) return false;
105  return fr >= 220000 && fr <= 399950;
106  }
107 
108  bool CComSystem::isValidComFrequency(const CFrequency &f)
109  {
110  if (f.isNull()) { return false; }
111  return isValidCivilAviationFrequency(f) || isValidMilitaryFrequency(f);
112  }
113 
114  bool CComSystem::isValid8_33kHzChannel(int fKHz)
115  {
116  const int lastDigits = static_cast<int>(fKHz) % 100;
117  return fKHz % 5 == 0 && lastDigits != 20 && lastDigits != 45 && lastDigits != 70 && lastDigits != 95;
118  }
119 
120  int CComSystem::round8_33kHzChannel(int fKHz)
121  {
122  if (!isValid8_33kHzChannel(fKHz))
123  {
124  const int diff = static_cast<int>(fKHz) % 5;
125  int lower = fKHz - diff;
126  if (!isValid8_33kHzChannel(lower)) { lower -= 5; }
127  Q_ASSERT_X(isValid8_33kHzChannel(lower), Q_FUNC_INFO, "Lower frequency not valid");
128 
129  int upper = fKHz + (5 - diff);
130  if (!isValid8_33kHzChannel(upper)) { upper += 5; }
131  Q_ASSERT_X(isValid8_33kHzChannel(upper), Q_FUNC_INFO, "Upper frequency not valid");
132 
133  const int lowerDiff = abs(fKHz - lower);
134  const int upperDiff = abs(fKHz - upper);
135 
136  fKHz = lowerDiff < upperDiff ? lower : upper;
137  fKHz = std::clamp(fKHz, 118000, 136990);
138  }
139  return fKHz;
140  }
141 
142  void CComSystem::roundToChannelSpacing(CFrequency &frequency, ChannelSpacing channelSpacing)
143  {
144  if (frequency.isNull()) { return; }
145  const double channelSpacingKHz = CComSystem::channelSpacingToFrequencyKHz(channelSpacing);
146  const double fKHz = frequency.valueRounded(CFrequencyUnit::kHz(), 0);
147 
148  if (channelSpacing == ChannelSpacing8_33KHz)
149  {
150  const int freqKHz = round8_33kHzChannel(fKHz);
151  frequency.switchUnit(CFrequencyUnit::kHz());
152  frequency.setCurrentUnitValue(freqKHz);
153  }
154  else
155  {
156  const int dDown = static_cast<int>(fKHz / channelSpacingKHz);
157  const double fDownKHz = dDown * channelSpacingKHz;
158  const double fUpKHz = (dDown + 1) * channelSpacingKHz;
159  const bool down = qAbs(fKHz - fDownKHz) < qAbs(fUpKHz - fKHz); // which is the closest value
160  const double fMHz(CMathUtils::round((down ? fDownKHz : fUpKHz) / 1000.0, 3));
161  frequency.switchUnit(CFrequencyUnit::MHz());
162  frequency.setCurrentUnitValue(fMHz);
163  }
164  }
165 
166  bool CComSystem::isExclusiveWithin8_33kHzChannel(const physical_quantities::CFrequency &freq)
167  {
168  const int freqKHz = freq.value(CFrequencyUnit::kHz());
169  if (freqKHz < 118000 || freqKHz >= 137000 || !isValid8_33kHzChannel(freqKHz)) { return false; }
170  return !isWithin25kHzChannel(freq);
171  }
172 
173  bool CComSystem::isWithin25kHzChannel(const physical_quantities::CFrequency &freq)
174  {
175  const int end = static_cast<int>(freq.value(CFrequencyUnit::kHz())) % 100;
176  return end == 0 || end == 25 || end == 50 || end == 75;
177  }
178 
179  bool CComSystem::isSameFrequency(const CFrequency &freq1, const CFrequency &freq2)
180  {
181  using namespace swift::misc::physical_quantities::Literals;
182  if (freq1.isNull() || freq2.isNull()) { return false; }
183 
184  // Normalize .x20 => .x25 and .70 => .x75
185  auto normalize = [](CFrequency freq) {
186  const int freq_end = static_cast<int>(freq.value(CFrequencyUnit::kHz())) % 100;
187  if (freq_end == 20 || freq_end == 70) { freq += 5_kHz; }
188  return freq;
189  };
190 
191  CFrequency normalized_freq1 = normalize(freq1);
192  CFrequency normalized_freq2 = normalize(freq2);
193 
194  if (normalized_freq1 == normalized_freq2) { return true; } // shortcut for many of such comparisons
195 
196  // Avoid precision errors in Hz range
197  return normalized_freq1.valueInteger(CFrequencyUnit::kHz()) ==
198  normalized_freq2.valueInteger(CFrequencyUnit::kHz());
199  }
200 
201  CFrequency CComSystem::parseComFrequency(const QString &input, CPqString::SeparatorMode sep)
202  {
203  if (input.isEmpty()) { return CFrequency::null(); }
204  CFrequency comFreq;
205  if (isDigitsOnlyString(input))
206  {
207  const double f = input.toDouble();
208  comFreq = CFrequency(f, f > 999 ? CFrequencyUnit::kHz() : CFrequencyUnit::MHz());
209  }
210  else
211  {
212  comFreq.parseFromString(input, sep);
213  if (comFreq.isNull())
214  {
215  bool ok {};
216  const double f = CPqString::parseNumber(input, ok, sep);
217  if (ok) { comFreq = CFrequency(f, f > 999 ? CFrequencyUnit::kHz() : CFrequencyUnit::MHz()); }
218  else { comFreq = CFrequency::null(); }
219  }
220  }
221 
222  if (comFreq.isNull()) { return CFrequency::null(); }
223  roundToChannelSpacing(comFreq, ChannelSpacing8_33KHz);
224  return isValidComFrequency(comFreq) ? comFreq : CFrequency::null();
225  }
226 
227  double CComSystem::channelSpacingToFrequencyKHz(ChannelSpacing channelSpacing)
228  {
229  switch (channelSpacing)
230  {
231  case ChannelSpacing50KHz: return 50.0;
232  case ChannelSpacing25KHz: return 25.0;
233  case ChannelSpacing8_33KHz: return 25.0 / 3.0;
234  default: qFatal("Wrong channel spacing"); return 0.0; // return just supressing compiler warning
235  }
236  }
237 } // namespace swift::misc::aviation
COM system (aka "radio")
Definition: comsystem.h:37
ChannelSpacing
Channel spacing frequency.
Definition: comsystem.h:47
static double round(double value, int digits)
Utility round method.
Definition: mathutils.cpp:18
static void registerMetadata()
Register metadata.
Definition: mixinmetatype.h:54
static CFrequencyUnit MHz()
Megahertz.
Definition: units.h:388
static CFrequencyUnit kHz()
Kilohertz.
Definition: units.h:379
static const CFrequency & FrequencyUnicom()
Unicom frequency.
Definition: constants.h:29
void parseFromString(const QString &value)
Parse value from string.
int valueInteger(MU unit) const
As integer value.
PQ & switchUnit(const MU &newUnit)
Change unit, and convert value to maintain the same quantity.
double value(MU unit) const
Value in given unit.
void setCurrentUnitValue(double value)
Set value in current unit.
double valueRounded(MU unit, int digits=-1) const
Rounded value in given unit.
SeparatorMode
Number separators / group separators.
Definition: pqstring.h:34
static double parseNumber(const QString &number, bool &success, SeparatorMode mode=SeparatorBestGuess)
Locale aware parsing.
Definition: pqstring.cpp:106
T::const_iterator end(const LockFreeReader< T > &reader)
Non-member begin() and end() for so LockFree containers can be used in ranged for loops.
Definition: lockfree.h:338
bool isDigitsOnlyString(const QString &testString)
String with digits only.
Definition: stringutils.h:167
bool isEmpty() const const
double toDouble(bool *ok) const const