diff --git a/changelog.md b/changelog.md
index 8eb68aa0..535dc50e 100644
--- a/changelog.md
+++ b/changelog.md
@@ -18,6 +18,7 @@
* **[Waypoints]** Allow user to add navigation waypoints where possible without degrading to a custom flight-plan
* **[Campaign Management]** Improve squadron retreat logic to account for parking-slot sizes
* **[Autoplanner]** Support for auto-planning Air Assaults
+* **[UI]** Improved frequency selector to support all modeled bands for every aircraft's intra-flight radio
## Fixes
* **[Mission Generation]** Anti-ship strikes should use "group attack" in their attack-task
diff --git a/game/radio/radios.py b/game/radio/radios.py
index a0f2e37c..cf02f040 100644
--- a/game/radio/radios.py
+++ b/game/radio/radios.py
@@ -190,8 +190,8 @@ RADIOS: List[Radio] = [
Radio(
"AN/ARC-222",
(
- RadioRange(MHz(30), MHz(88), kHz(25), Modulation.FM),
RadioRange(MHz(116), MHz(152), kHz(25), Modulation.AM),
+ RadioRange(MHz(30), MHz(88), kHz(25), Modulation.FM),
),
),
Radio("SCR-522", (RadioRange(MHz(100), MHz(156), kHz(25), Modulation.AM),)),
@@ -200,8 +200,8 @@ RADIOS: List[Radio] = [
Radio(
"TRT ERA 7000 V/UHF",
(
- RadioRange(MHz(118), MHz(150), kHz(25), Modulation.AM),
RadioRange(MHz(225), MHz(400), kHz(25), Modulation.AM),
+ RadioRange(MHz(118), MHz(150), kHz(25), Modulation.AM),
),
),
Radio(
@@ -247,10 +247,10 @@ RADIOS: List[Radio] = [
Radio(
"R-863",
(
- RadioRange(MHz(100), MHz(150), kHz(25), Modulation.AM),
RadioRange(MHz(220), MHz(400), kHz(25), Modulation.AM),
- RadioRange(MHz(100), MHz(150), kHz(25), Modulation.FM),
+ RadioRange(MHz(100), MHz(150), kHz(25), Modulation.AM),
RadioRange(MHz(220), MHz(400), kHz(25), Modulation.FM),
+ RadioRange(MHz(100), MHz(150), kHz(25), Modulation.FM),
),
),
# UH-1H
@@ -263,8 +263,8 @@ RADIOS: List[Radio] = [
Radio(
"V/UHF TRAP 136",
(
- RadioRange(MHz(118), MHz(144), kHz(25), Modulation.AM),
RadioRange(MHz(225), MHz(400), kHz(25), Modulation.AM),
+ RadioRange(MHz(118), MHz(144), kHz(25), Modulation.AM),
),
),
Radio("UHF TRAP 137B", (RadioRange(MHz(225), MHz(400), kHz(25), Modulation.AM),)),
@@ -272,9 +272,9 @@ RADIOS: List[Radio] = [
Radio(
"R-800",
(
- RadioRange(MHz(30), MHz(88), kHz(25), Modulation.AM),
- RadioRange(MHz(108), MHz(174), kHz(25), Modulation.AM),
RadioRange(MHz(225), MHz(400), kHz(25), Modulation.AM),
+ RadioRange(MHz(108), MHz(174), kHz(25), Modulation.AM),
+ RadioRange(MHz(30), MHz(88), kHz(25), Modulation.AM),
),
),
# MB-339A
@@ -294,18 +294,11 @@ RADIOS: List[Radio] = [
"SRT-651/N",
(
RadioRange(
- MHz(30),
- MHz(88),
+ MHz(225),
+ MHz(400),
kHz(25),
- Modulation.FM,
- frozenset((MHz(40, 500),)),
- ),
- RadioRange(
- MHz(108),
- MHz(156),
- kHz(25),
- Modulation.AM,
- frozenset((MHz(121, 500),)),
+ Modulation.AM, # Actually AM/FM, but we can't represent that.
+ frozenset((MHz(243),)),
),
RadioRange(
MHz(156),
@@ -315,11 +308,18 @@ RADIOS: List[Radio] = [
frozenset((MHz(156, 800),)),
),
RadioRange(
- MHz(225),
- MHz(400),
+ MHz(108),
+ MHz(156),
kHz(25),
- Modulation.AM, # Actually AM/FM, but we can't represent that.
- frozenset((MHz(243),)),
+ Modulation.AM,
+ frozenset((MHz(121, 500),)),
+ ),
+ RadioRange(
+ MHz(30),
+ MHz(88),
+ kHz(25),
+ Modulation.FM,
+ frozenset((MHz(40, 500),)),
),
),
),
diff --git a/qt_ui/widgets/QFrequencyWidget.py b/qt_ui/widgets/QFrequencyWidget.py
index 148b2c3a..0e57dddc 100644
--- a/qt_ui/widgets/QFrequencyWidget.py
+++ b/qt_ui/widgets/QFrequencyWidget.py
@@ -49,22 +49,18 @@ class QFrequencyWidget(QWidget):
return f"FREQ: {freq}"
def open_freq_dialog(self) -> None:
- range = RadioRange(MHz(100), MHz(400), kHz(25))
+ ranges = [RadioRange(MHz(30), MHz(400), kHz(25))]
if isinstance(self.ct, Flight):
if self.ct.unit_type.intra_flight_radio is not None:
- range = self.ct.unit_type.intra_flight_radio.ranges[0]
- self.frequency_dialog = QRadioFrequencyDialog(self, self.ct, range)
+ ranges = self.ct.unit_type.intra_flight_radio.ranges
+ self.frequency_dialog = QRadioFrequencyDialog(self, self.ct, ranges)
self.frequency_dialog.accepted.connect(self.assign_frequency)
self.frequency_dialog.show()
def assign_frequency(self) -> None:
hz = round(self.frequency_dialog.frequency_input.value() * 10**6)
self._try_remove()
- mod = RadioFrequency.modulation
- if isinstance(self.ct, Flight):
- if self.ct.unit_type.intra_flight_radio is not None:
- range = self.ct.unit_type.intra_flight_radio.ranges[0]
- mod = range.modulation
+ mod = self.frequency_dialog.frequency_modulation.currentData()
self.ct.frequency = RadioFrequency(hertz=hz, modulation=mod)
self.gm.allocated_freqs.append(self.ct.frequency)
self.freq.setText(self._get_label_text())
diff --git a/qt_ui/widgets/QLink4Widget.py b/qt_ui/widgets/QLink4Widget.py
index dc0647ed..bbced2c2 100644
--- a/qt_ui/widgets/QLink4Widget.py
+++ b/qt_ui/widgets/QLink4Widget.py
@@ -46,8 +46,8 @@ class QLink4Widget(QWidget):
return f"LINK4: {freq}"
def open_freq_dialog(self) -> None:
- range = RadioRange(MHz(225), MHz(400), kHz(25))
- self.frequency_dialog = QRadioFrequencyDialog(self, self.cp, range, link4=True)
+ ranges = [RadioRange(MHz(225), MHz(400), kHz(25))]
+ self.frequency_dialog = QRadioFrequencyDialog(self, self.cp, ranges, link4=True)
self.frequency_dialog.accepted.connect(self.assign_frequency)
self.frequency_dialog.show()
diff --git a/qt_ui/windows/QRadioFrequencyDialog.py b/qt_ui/windows/QRadioFrequencyDialog.py
index 3c69a212..c35c274a 100644
--- a/qt_ui/windows/QRadioFrequencyDialog.py
+++ b/qt_ui/windows/QRadioFrequencyDialog.py
@@ -1,12 +1,14 @@
-from typing import Optional
+from typing import Optional, Iterable
from PySide2.QtCore import Qt, QLocale
+from PySide2.QtGui import QIcon
from PySide2.QtWidgets import (
QDialog,
QPushButton,
QLabel,
QHBoxLayout,
QDoubleSpinBox,
+ QComboBox,
)
from game.radio.Link4Container import Link4Container
@@ -15,12 +17,80 @@ from game.radio.radios import RadioRange, kHz, MHz
from qt_ui.uiconstants import EVENT_ICONS
+class QFrequencySpinbox(QDoubleSpinBox):
+ def __init__(self, ranges: Iterable[RadioRange]) -> None:
+ super().__init__()
+ self.setLocale(QLocale(QLocale.Language.English))
+ self.setDecimals(3)
+ self.ranges = list(ranges)
+ first = True
+ for r in ranges:
+ if r.minimum.mhz < self.minimum():
+ self.setMinimum(r.minimum.mhz)
+ if self.maximum() < r.maximum.mhz:
+ self.setMaximum(r.maximum.mhz)
+ if first:
+ self.setSingleStep(r.step.mhz)
+ self.setValue(r.minimum.mhz)
+ first = False
+
+ def correct_value(self, value: float) -> None:
+ for r in self.ranges:
+ if r.maximum.mhz == value:
+ self.setValue(value - r.step.mhz)
+ return
+
+ def stepBy(self, steps: int) -> None:
+ new_value = self.check_value(self.value() + (steps * self.singleStep()))
+ self.setValue(new_value)
+
+ def check_value(self, value: float) -> float:
+ for r in self.ranges:
+ if r.minimum.mhz <= value < r.maximum.mhz:
+ self.setSingleStep(r.step.mhz)
+ return value
+ minimums = [m for m in set(r.minimum.mhz for r in self.ranges) if m > value]
+ maximums = [m for m in set(r.maximum.mhz for r in self.ranges) if m <= value]
+ if not minimums or not maximums:
+ return self.value()
+ smallest_min = min(minimums)
+ largest_max = max(maximums)
+ if largest_max <= value < smallest_min:
+ if value < self.value():
+ rs = [r for r in self.ranges if r.maximum.mhz == largest_max]
+ value = largest_max - rs[0].step.mhz
+ else:
+ rs = [r for r in self.ranges if r.minimum.mhz == smallest_min]
+ value = smallest_min
+ r = rs[0]
+ self.setSingleStep(r.step.mhz)
+ return value
+ raise RuntimeError()
+
+
+class QFrequencyModulationBox(QComboBox):
+ def __init__(self, ranges: Iterable[RadioRange], freq: float) -> None:
+ super().__init__()
+ self.setMaximumWidth(60)
+ self.ranges = list(ranges)
+ self.update_modulations(freq)
+
+ def update_modulations(self, freq: float) -> None:
+ self.modulations = set(
+ r.modulation for r in self.ranges if r.minimum.mhz <= freq < r.maximum.mhz
+ )
+ self.setEnabled(len(self.modulations) > 1)
+ self.clear()
+ for i, m in enumerate(sorted(self.modulations, key=lambda x: x.name)):
+ self.addItem(QIcon(), m.name, m)
+
+
class QRadioFrequencyDialog(QDialog):
def __init__(
self,
parent=None,
container: Optional[RadioFrequencyContainer] = None,
- range: RadioRange = RadioRange(MHz(225), MHz(400), kHz(25)),
+ ranges: Iterable[RadioRange] = tuple([RadioRange(MHz(225), MHz(400), kHz(25))]),
link4: bool = False,
) -> None:
super().__init__(parent=parent)
@@ -36,20 +106,18 @@ class QRadioFrequencyDialog(QDialog):
layout = QHBoxLayout()
self.frequency_label = QLabel("FREQ (MHz):")
- self.frequency_input = QDoubleSpinBox()
- self.frequency_input.setRange(
- range.minimum.mhz, range.maximum.mhz - range.step.mhz
+ self.frequency_input = QFrequencySpinbox(ranges)
+ self.frequency_modulation = QFrequencyModulationBox(
+ ranges, self.frequency_input.value()
)
- self.frequency_input.setSingleStep(range.step.mhz)
- self.frequency_input.setDecimals(3)
- self.frequency_input.setLocale(QLocale(QLocale.Language.English))
- if range.minimum.mhz <= 225.0 < range.maximum.mhz:
- self.frequency_input.setValue(225.0)
- else:
- mid = range.minimum.mhz + (range.maximum.mhz - range.minimum.mhz) / 2
- self.frequency_input.setValue(mid)
+ self.frequency_input.valueChanged.connect(
+ self.frequency_modulation.update_modulations
+ )
+ self.frequency_input.valueChanged.connect(self.frequency_input.correct_value)
+
layout.addWidget(self.frequency_label)
layout.addWidget(self.frequency_input)
+ layout.addWidget(self.frequency_modulation)
self.create_button = QPushButton("Save")
self.create_button.clicked.connect(self.accept)