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 CComSystem(
67  CModulator::NameCom1(),
69  physical_quantities::CFrequency(standbyFrequencyMHz < 0 ? activeFrequencyMHz : standbyFrequencyMHz,
71  }
72 
73  CComSystem CComSystem::getCom1System(const CFrequency &activeFrequency, const CFrequency &standbyFrequency)
74  {
75  return CComSystem(CModulator::NameCom1(), activeFrequency,
76  standbyFrequency.isNull() ? activeFrequency : standbyFrequency);
77  }
78 
79  CComSystem CComSystem::getCom2System(double activeFrequencyMHz, double standbyFrequencyMHz)
80  {
81  return CComSystem(
82  CModulator::NameCom2(),
84  physical_quantities::CFrequency(standbyFrequencyMHz < 0 ? activeFrequencyMHz : standbyFrequencyMHz,
86  }
87 
88  CComSystem CComSystem::getCom2System(const CFrequency &activeFrequency, const CFrequency &standbyFrequency)
89  {
90  return CComSystem(CModulator::NameCom2(), activeFrequency,
91  standbyFrequency.isNull() ? activeFrequency : standbyFrequency);
92  }
93 
94  bool CComSystem::isValidCivilAviationFrequency(const CFrequency &f)
95  {
96  if (f.isNull()) return false;
97 
98  // comparsion in int avoids double compare issues
100  return fr >= 118000 && fr <= 136990;
101  }
102 
103  bool CComSystem::isValidMilitaryFrequency(const CFrequency &f)
104  {
105  if (f.isNull()) return false;
107  return fr >= 220000 && fr <= 399950;
108  }
109 
110  bool CComSystem::isValidComFrequency(const CFrequency &f)
111  {
112  if (f.isNull()) { return false; }
113  return isValidCivilAviationFrequency(f) || isValidMilitaryFrequency(f);
114  }
115 
116  bool CComSystem::isValid8_33kHzChannel(int fKHz)
117  {
118  const int lastDigits = static_cast<int>(fKHz) % 100;
119  return fKHz % 5 == 0 && lastDigits != 20 && lastDigits != 45 && lastDigits != 70 && lastDigits != 95;
120  }
121 
122  int CComSystem::round8_33kHzChannel(int fKHz)
123  {
124  if (!isValid8_33kHzChannel(fKHz))
125  {
126  const int diff = static_cast<int>(fKHz) % 5;
127  int lower = fKHz - diff;
128  if (!isValid8_33kHzChannel(lower)) { lower -= 5; }
129  Q_ASSERT_X(isValid8_33kHzChannel(lower), Q_FUNC_INFO, "Lower frequency not valid");
130 
131  int upper = fKHz + (5 - diff);
132  if (!isValid8_33kHzChannel(upper)) { upper += 5; }
133  Q_ASSERT_X(isValid8_33kHzChannel(upper), Q_FUNC_INFO, "Upper frequency not valid");
134 
135  const int lowerDiff = abs(fKHz - lower);
136  const int upperDiff = abs(fKHz - upper);
137 
138  fKHz = lowerDiff < upperDiff ? lower : upper;
139  fKHz = std::clamp(fKHz, 118000, 136990);
140  }
141  return fKHz;
142  }
143 
144  void CComSystem::roundToChannelSpacing(CFrequency &frequency, ChannelSpacing channelSpacing)
145  {
146  if (frequency.isNull()) { return; }
147  const double channelSpacingKHz = CComSystem::channelSpacingToFrequencyKHz(channelSpacing);
148  const double fKHz = frequency.valueRounded(CFrequencyUnit::kHz(), 0);
149 
150  if (channelSpacing == ChannelSpacing8_33KHz)
151  {
152  const int freqKHz = round8_33kHzChannel(fKHz);
153  frequency.switchUnit(CFrequencyUnit::kHz());
154  frequency.setCurrentUnitValue(freqKHz);
155  }
156  else
157  {
158  const int dDown = static_cast<int>(fKHz / channelSpacingKHz);
159  const double fDownKHz = dDown * channelSpacingKHz;
160  const double fUpKHz = (dDown + 1) * channelSpacingKHz;
161  const bool down = qAbs(fKHz - fDownKHz) < qAbs(fUpKHz - fKHz); // which is the closest value
162  const double fMHz(CMathUtils::round((down ? fDownKHz : fUpKHz) / 1000.0, 3));
163  frequency.switchUnit(CFrequencyUnit::MHz());
164  frequency.setCurrentUnitValue(fMHz);
165  }
166  }
167 
168  bool CComSystem::isExclusiveWithin8_33kHzChannel(const physical_quantities::CFrequency &freq)
169  {
170  const int freqKHz = freq.value(CFrequencyUnit::kHz());
171  if (freqKHz < 118000 || freqKHz >= 137000 || !isValid8_33kHzChannel(freqKHz)) { return false; }
172  return !isWithin25kHzChannel(freq);
173  }
174 
175  bool CComSystem::isWithin25kHzChannel(const physical_quantities::CFrequency &freq)
176  {
177  const int end = static_cast<int>(freq.value(CFrequencyUnit::kHz())) % 100;
178  return end == 0 || end == 25 || end == 50 || end == 75;
179  }
180 
181  bool CComSystem::isSameFrequency(const CFrequency &freq1, const CFrequency &freq2)
182  {
183  using namespace swift::misc::physical_quantities::Literals;
184  if (freq1.isNull() || freq2.isNull()) { return false; }
185 
186  // Normalize .x20 => .x25 and .70 => .x75
187  auto normalize = [](CFrequency freq) {
188  const int freq_end = static_cast<int>(freq.value(CFrequencyUnit::kHz())) % 100;
189  if (freq_end == 20 || freq_end == 70) { freq += 5_kHz; }
190  return freq;
191  };
192 
193  CFrequency normalized_freq1 = normalize(freq1);
194  CFrequency normalized_freq2 = normalize(freq2);
195 
196  if (normalized_freq1 == normalized_freq2) { return true; } // shortcut for many of such comparisons
197 
198  // Avoid precision errors in Hz range
199  return normalized_freq1.valueInteger(CFrequencyUnit::kHz()) ==
200  normalized_freq2.valueInteger(CFrequencyUnit::kHz());
201  }
202 
203  CFrequency CComSystem::parseComFrequency(const QString &input, CPqString::SeparatorMode sep)
204  {
205  if (input.isEmpty()) { return CFrequency::null(); }
206  CFrequency comFreq;
207  if (isDigitsOnlyString(input))
208  {
209  const double f = input.toDouble();
210  comFreq = CFrequency(f, f > 999 ? CFrequencyUnit::kHz() : CFrequencyUnit::MHz());
211  }
212  else
213  {
214  comFreq.parseFromString(input, sep);
215  if (comFreq.isNull())
216  {
217  bool ok;
218  const double f = CPqString::parseNumber(input, ok, sep);
219  if (ok) { comFreq = CFrequency(f, f > 999 ? CFrequencyUnit::kHz() : CFrequencyUnit::MHz()); }
220  else { comFreq = CFrequency::null(); }
221  }
222  }
223 
224  if (comFreq.isNull()) { return CFrequency::null(); }
225  roundToChannelSpacing(comFreq, ChannelSpacing8_33KHz);
226  return isValidComFrequency(comFreq) ? comFreq : CFrequency::null();
227  }
228 
229  double CComSystem::channelSpacingToFrequencyKHz(ChannelSpacing channelSpacing)
230  {
231  switch (channelSpacing)
232  {
233  case ChannelSpacing50KHz: return 50.0;
234  case ChannelSpacing25KHz: return 25.0;
235  case ChannelSpacing8_33KHz: return 25.0 / 3.0;
236  default: qFatal("Wrong channel spacing"); return 0.0; // return just supressing compiler warning
237  }
238  }
239 } // 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:56
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:168