From 8d253acb3390606deedb80dc14adec46599fc44b Mon Sep 17 00:00:00 2001
From: ldam <ldam@passcal.nmt.edu>
Date: Thu, 27 Mar 2025 13:35:55 -0600
Subject: [PATCH] switch ZNE and UVW, remove ABC, write
 _check_included_channel_group to make check_masspos smaller and readable

---
 sohstationviewer/conf/config_processor.py     | 14 +--
 sohstationviewer/controller/processing.py     |  2 +-
 sohstationviewer/controller/util.py           | 14 +--
 sohstationviewer/model/data_loader.py         |  4 +-
 .../model/general_data/general_data.py        | 12 +--
 sohstationviewer/model/mseed_data/mseed.py    |  4 +-
 .../model/mseed_data/mseed_reader.py          | 16 ++--
 sohstationviewer/model/reftek_data/reftek.py  |  8 +-
 .../model/reftek_data/reftek_helper.py        | 20 ++--
 sohstationviewer/view/main_window.py          | 16 ++--
 sohstationviewer/view/ui/main_ui.py           | 23 +++--
 sohstationviewer/view/util/functions.py       | 94 +++++++++----------
 12 files changed, 110 insertions(+), 117 deletions(-)

diff --git a/sohstationviewer/conf/config_processor.py b/sohstationviewer/conf/config_processor.py
index 51b8f1a51..a6947563b 100644
--- a/sohstationviewer/conf/config_processor.py
+++ b/sohstationviewer/conf/config_processor.py
@@ -23,8 +23,8 @@ white = False
 min_gap_length =
 
 [Channels]
-mp123zne = False
-mp456uvw = False
+mp123uvw = False
+mp456zne = False
 all_waveform_chans = False
 ds1 = False
 ds2 = False
@@ -68,7 +68,7 @@ class ConfigProcessor:
             'from_data_card', 'data', 'sdata', 'log',
             'black', 'white',
             'min_gap_length',
-            'mp123zne', 'mp456uvw',
+            'mp123uvw', 'mp456zne',
             'all_waveform_chans', 'ds1', 'ds2', 'ds3', 'ds4',
             'ds5', 'ds6', 'ds7', 'ds8', 'mseed_wildcard',
             'plot_tps', 'plot_raw',
@@ -224,11 +224,11 @@ class ConfigProcessor:
             window.config.get('Gap', 'min_gap_length')
         )
 
-        window.mass_pos_123zne_check_box.setChecked(
-            get_bool('Channels', 'mp123zne')
+        window.mass_pos_123uvw_check_box.setChecked(
+            get_bool('Channels', 'mp123uvw')
         )
-        window.mass_pos_456uvw_check_box.setChecked(
-            get_bool('Channels', 'mp456uvw')
+        window.mass_pos_456zne_check_box.setChecked(
+            get_bool('Channels', 'mp456zne')
         )
         window.all_wf_chans_check_box.setChecked(
             get_bool('Channels', 'all_waveform_chans')
diff --git a/sohstationviewer/controller/processing.py b/sohstationviewer/controller/processing.py
index 044cd6e74..03db2dc45 100644
--- a/sohstationviewer/controller/processing.py
+++ b/sohstationviewer/controller/processing.py
@@ -112,7 +112,7 @@ def read_mseed_channels(tracking_box: QTextBrowser, list_of_dir: List[str],
         Since channels for RT130 is hard code, this function won't be applied
         for it.
     Note that Mass position channels are excluded because the default
-        include_mp123zne and include_mp456uvw for MSeed are False
+        include_mp123uvw and include_mp456zne for MSeed are False
     :param tracking_box: widget to display tracking info
     :param list_of_dir: list of directories selected by users
     :param is_multiplex: flag that tell if data is multiplex
diff --git a/sohstationviewer/controller/util.py b/sohstationviewer/controller/util.py
index a46f6d391..82a16c28c 100644
--- a/sohstationviewer/controller/util.py
+++ b/sohstationviewer/controller/util.py
@@ -331,15 +331,15 @@ def check_data_sdata(root_dir: str) -> bool:
 
 
 def check_chan(chan_id: str, req_soh_chans: List[str], req_wf_chans: List[str],
-               include_mp123zne: bool, include_mp456uvw: bool) \
+               include_mp123uvw: bool, include_mp456zne: bool) \
         -> Union[str, bool]:
     """
     Check if chanID is a requested channel.
     :param chan_id: str - channel ID
     :param req_soh_chans: list of str - requested SOH channels
     :param req_wf_chans: list of str - requested waveform channels
-    :param include_mp123zne: if mass position channels 1,2,3 are requested
-    :param include_mp456uvw: if mass position channels 4,5,6 are requested
+    :param include_mp123uvw: if mass position channels 1,2,3 are requested
+    :param include_mp456zne: if mass position channels 4,5,6 are requested
 
     :return: str/bool -
         'WF' if chanID is a requested waveform channel,
@@ -348,11 +348,11 @@ def check_chan(chan_id: str, req_soh_chans: List[str], req_wf_chans: List[str],
         False otherwise.
     """
     if chan_id.startswith('VM'):
-        if (not include_mp123zne and
-                chan_id[-1] in ['1', '2', '3', 'Z', 'N', 'E', 'A', 'B', 'C']):
+        if (not include_mp123uvw and
+                chan_id[-1] in ['1', '2', '3', 'U', 'V', 'W']):
             return False
-        if (not include_mp456uvw
-                and chan_id[-1] in ['4', '5', '6', 'U', 'V', 'W']):
+        if (not include_mp456zne
+                and chan_id[-1] in ['4', '5', '6', 'Z', 'N', 'E']):
             return False
         return 'MP'
 
diff --git a/sohstationviewer/model/data_loader.py b/sohstationviewer/model/data_loader.py
index 7c420d0c0..b095018b5 100644
--- a/sohstationviewer/model/data_loader.py
+++ b/sohstationviewer/model/data_loader.py
@@ -99,8 +99,8 @@ class DataLoaderWorker(QtCore.QObject):
                 self.list_of_rt130_paths, req_wf_chans=self.req_wf_chans,
                 req_soh_chans=self.req_soh_chans, gap_minimum=self.gap_minimum,
                 read_start=self.read_start, read_end=self.read_end,
-                include_mp123zne=self.include_mp123,
-                include_mp456uvw=self.include_mp456,
+                include_mp123uvw=self.include_mp123,
+                include_mp456zne=self.include_mp456,
                 rt130_waveform_data_req=self.rt130_waveform_data_req,
                 rt130_log_files=self.rt130_log_files,
                 include_masspos_in_soh_messages=(
diff --git a/sohstationviewer/model/general_data/general_data.py b/sohstationviewer/model/general_data/general_data.py
index 096c745dc..87ff78ff9 100644
--- a/sohstationviewer/model/general_data/general_data.py
+++ b/sohstationviewer/model/general_data/general_data.py
@@ -44,8 +44,8 @@ class GeneralData():
                  gap_minimum: float = None,
                  read_start: Optional[float] = UTCDateTime(0).timestamp,
                  read_end: Optional[float] = UTCDateTime().timestamp,
-                 include_mp123zne: bool = False,
-                 include_mp456uvw: bool = False,
+                 include_mp123uvw: bool = False,
+                 include_mp456zne: bool = False,
                  rt130_waveform_data_req: bool = False,
                  rt130_log_files: List[Path] = [],
                  include_masspos_in_soh_messages: bool = False,
@@ -66,8 +66,8 @@ class GeneralData():
         :param req_soh_chans: requested SOH channel list
         :param read_start: requested start time to read
         :param read_end: requested end time to read
-        :param include_mp123zne: if mass position channels 1,2,3 are requested
-        :param include_mp456uvw: if mass position channels 4,5,6 are requested
+        :param include_mp123uvw: if mass position channels 1,2,3 are requested
+        :param include_mp456zne: if mass position channels 4,5,6 are requested
         :param rt130_waveform_data_req: flag for RT130 to read waveform data
         :param rt130_log_files: list of paths to log files chosen by the user
         :param include_masspos_in_soh_messages: whether to include mass
@@ -90,8 +90,8 @@ class GeneralData():
         self.gap_minimum = gap_minimum
         self.read_start = read_start
         self.read_end = read_end
-        self.include_mp123zne = include_mp123zne
-        self.include_mp456uvw = include_mp456uvw
+        self.include_mp123uvw = include_mp123uvw
+        self.include_mp456zne = include_mp456zne
         self.rt130_waveform_data_req = rt130_waveform_data_req
         self.rt130_log_files = rt130_log_files
         self.include_masspos_in_soh_messages = include_masspos_in_soh_messages
diff --git a/sohstationviewer/model/mseed_data/mseed.py b/sohstationviewer/model/mseed_data/mseed.py
index ecb65f28d..e7600531d 100644
--- a/sohstationviewer/model/mseed_data/mseed.py
+++ b/sohstationviewer/model/mseed_data/mseed.py
@@ -80,8 +80,8 @@ class MSeed(GeneralData):
             is_multiplex=self.is_multiplex,
             req_soh_chans=self.req_soh_chans,
             req_wf_chans=self.req_wf_chans,
-            include_mp123zne=self.include_mp123zne,
-            include_mp456uvw=self.include_mp456uvw,
+            include_mp123uvw=self.include_mp123uvw,
+            include_mp456zne=self.include_mp456zne,
             soh_data=self.soh_data,
             mass_pos_data=self.mass_pos_data,
             waveform_data=self.waveform_data,
diff --git a/sohstationviewer/model/mseed_data/mseed_reader.py b/sohstationviewer/model/mseed_data/mseed_reader.py
index 45a3243ea..409fa9b24 100644
--- a/sohstationviewer/model/mseed_data/mseed_reader.py
+++ b/sohstationviewer/model/mseed_data/mseed_reader.py
@@ -42,8 +42,8 @@ class MSeedReader:
                  is_multiplex: Optional[bool] = None,
                  req_soh_chans: List[str] = [],
                  req_wf_chans: List[str] = [],
-                 include_mp123zne: bool = False,
-                 include_mp456uvw: bool = False,
+                 include_mp123uvw: bool = False,
+                 include_mp456zne: bool = False,
                  soh_data: Dict = {},
                  mass_pos_data: Dict = {},
                  waveform_data: Dict = {},
@@ -66,8 +66,8 @@ class MSeedReader:
         :param is_multiplex: multiplex status of the file's data_type
         :param req_soh_chans: requested SOH channel list
         :param req_wf_chans: requested waveform channel list
-        :param include_mp123zne: if mass position channels 1,2,3 are requested
-        :param include_mp456uvw: if mass position channels 4,5,6 are requested
+        :param include_mp123uvw: if mass position channels 1,2,3 are requested
+        :param include_mp456zne: if mass position channels 4,5,6 are requested
         :param soh_data: data dict of SOH
         :param mass_pos_data: data dict of mass position
         :param waveform_data: data dict of waveform
@@ -81,8 +81,8 @@ class MSeedReader:
         self.gap_minimum = gap_minimum
         self.req_soh_chans = req_soh_chans
         self.req_wf_chans = req_wf_chans
-        self.include_mp123zne = include_mp123zne
-        self.include_mp456uvw = include_mp456uvw
+        self.include_mp123uvw = include_mp123uvw
+        self.include_mp456zne = include_mp456zne
         self.soh_data = soh_data
         self.mass_pos_data = mass_pos_data
         self.waveform_data = waveform_data
@@ -95,14 +95,14 @@ class MSeedReader:
     def get_data_dict(self, metadata: RecordMetadata) -> Dict:
         """
         Find which data_dict to add data to from req_soh_chans, req_wf_chans,
-            include_mp123zne, include_mp456uvw, samplerate
+            include_mp123uvw, include_mp456zne, samplerate
         :param metadata: record's metadata
         :return: data_dict to add data
         """
         chan_id = metadata.channel
         sample_rate = metadata.sample_rate
         chan_type = check_chan(chan_id, self.req_soh_chans, self.req_wf_chans,
-                               self.include_mp123zne, self.include_mp456uvw)
+                               self.include_mp123uvw, self.include_mp456zne)
         if chan_type == 'SOH':
             if self.req_soh_chans == [] and sample_rate > 1:
                 # If 'All chans' is selected for SOH, channel with samplerate>1
diff --git a/sohstationviewer/model/reftek_data/reftek.py b/sohstationviewer/model/reftek_data/reftek.py
index c85d0d441..fe122760b 100755
--- a/sohstationviewer/model/reftek_data/reftek.py
+++ b/sohstationviewer/model/reftek_data/reftek.py
@@ -241,9 +241,9 @@ class RT130(GeneralData):
                      'data': data, 'times': times}
             if masspos_chan not in self.mass_pos_data[data_set_id]:
                 if masspos_chan[-1] in ['1', '2', '3']:
-                    is_visible = self.include_mp123zne
+                    is_visible = self.include_mp123uvw
                 else:
-                    is_visible = self.include_mp456uvw
+                    is_visible = self.include_mp456zne
                 self.mass_pos_data[data_set_id][masspos_chan] = (
                     {'tracesInfo': [],
                      'visible': is_visible}
@@ -462,12 +462,12 @@ class RT130(GeneralData):
             rt130, cur_data_set_id, self.read_start, self.read_end,
             self.stream_header_by_data_set_id_chan,
             cur_data_dict, self.data_time[cur_data_set_id],
-            self.include_mp123zne, self.include_mp456uvw)
+            self.include_mp123uvw, self.include_mp456zne)
         if avail_trace_indexes == []:
             return
         read_reftek_stream(
             rt130, avail_trace_indexes, cur_data_dict,
-            self.include_mp123zne, self.include_mp456uvw)
+            self.include_mp123uvw, self.include_mp456zne)
 
     def prepare_soh_data_from_log_data(self) -> None:
         """
diff --git a/sohstationviewer/model/reftek_data/reftek_helper.py b/sohstationviewer/model/reftek_data/reftek_helper.py
index 67f2000c8..44bda25b4 100644
--- a/sohstationviewer/model/reftek_data/reftek_helper.py
+++ b/sohstationviewer/model/reftek_data/reftek_helper.py
@@ -53,7 +53,7 @@ def check_reftek_header(
         starttime: UTCDateTime, endtime: UTCDateTime,
         stream_header_by_data_set_id_chan: Dict[str, Dict[str, Stream]],
         cur_data_dict: Dict, cur_data_time: List[float],
-        include_mp123zne: bool, include_mp456uvw: bool):
+        include_mp123uvw: bool, include_mp456zne: bool):
     """
     Read mseed headers of a file from the given rt130 object
         to check for time, create stream_header for retrieving gaps later.
@@ -71,13 +71,13 @@ def check_reftek_header(
     :param cur_data_dict: waveform_data/mass_pos_data of the current
         data_set_id
     :param cur_data_time: data_time of the current data_set_id
-    :param include_mp123zne: if mass position channels 1,2,3 are requested
-    :param include_mp456uvw: if mass position channels 4,5,6 are requested
+    :param include_mp123uvw: if mass position channels 1,2,3 are requested
+    :param include_mp456zne: if mass position channels 4,5,6 are requested
     """
     stream = DecimatedReftek130.to_stream(
         rt130,
-        include_mp123=include_mp123zne,
-        include_mp456=include_mp456uvw,
+        include_mp123=include_mp123uvw,
+        include_mp456=include_mp456zne,
         headonly=True,
         verbose=False,
         sort_permuted_package_sequence=True)
@@ -116,7 +116,7 @@ def check_reftek_header(
 def read_reftek_stream(
         rt130: DecimatedReftek130,
         avail_trace_indexes: List[int], cur_data_dict: Dict,
-        include_mp123zne: bool, include_mp456uvw: bool):
+        include_mp123uvw: bool, include_mp456zne: bool):
     """
     Read traces of a file from the given rt130 object for the index in
         avail_trace_indexes.
@@ -125,14 +125,14 @@ def read_reftek_stream(
     :param avail_trace_indexes: index of traces to get
     :param cur_data_dict: waveform_data/mass_pos_data of the current
         data_set_id
-    :param include_mp123zne: if mass position channels 1,2,3 are requested
-    :param include_mp456uvw: if mass position channels 4,5,6 are requested
+    :param include_mp123uvw: if mass position channels 1,2,3 are requested
+    :param include_mp456zne: if mass position channels 4,5,6 are requested
     """
     # TODO: rewrite reftek to read stream with start and end time
     stream = DecimatedReftek130.to_stream(
         rt130,
-        include_mp123=include_mp123zne,
-        include_mp456=include_mp456uvw,
+        include_mp123=include_mp123uvw,
+        include_mp456=include_mp456zne,
         headonly=False,
         verbose=False,
         sort_permuted_package_sequence=True)
diff --git a/sohstationviewer/view/main_window.py b/sohstationviewer/view/main_window.py
index 6ee524b28..ffacfb4a5 100755
--- a/sohstationviewer/view/main_window.py
+++ b/sohstationviewer/view/main_window.py
@@ -829,8 +829,8 @@ class MainWindow(QtWidgets.QMainWindow, UIMainWindow):
             gap_minimum=self.gap_minimum,
             read_start=self.start_tm,
             read_end=self.end_tm,
-            include_mp123=self.mass_pos_123zne_check_box.isChecked(),
-            include_mp456=self.mass_pos_456uvw_check_box.isChecked(),
+            include_mp123=self.mass_pos_123uvw_check_box.isChecked(),
+            include_mp456=self.mass_pos_456zne_check_box.isChecked(),
             rt130_waveform_data_req=rt130_waveform_data_req,
             rt130_log_files=self.rt130_log_files,
             include_masspos_in_soh_messages=(
@@ -1014,8 +1014,8 @@ class MainWindow(QtWidgets.QMainWindow, UIMainWindow):
         try:
             check_masspos(d_obj.mass_pos_data[selected_data_set_id],
                           selected_data_set_id,
-                          self.mass_pos_123zne_check_box.isChecked(),
-                          self.mass_pos_456uvw_check_box.isChecked())
+                          self.mass_pos_123uvw_check_box.isChecked(),
+                          self.mass_pos_456zne_check_box.isChecked())
         except Exception as e:
             self.processing_log.append((str(e), LogType.WARNING))
 
@@ -1234,10 +1234,10 @@ class MainWindow(QtWidgets.QMainWindow, UIMainWindow):
                         str(self.background_white_radio_button.isChecked()))
         self.config.set('Gap', 'min_gap_length',
                         self.gap_len_line_edit.text())
-        self.config.set('Channels', 'mp123zne',
-                        str(self.mass_pos_123zne_check_box.isChecked()))
-        self.config.set('Channels', 'mp456uvw',
-                        str(self.mass_pos_456uvw_check_box.isChecked()))
+        self.config.set('Channels', 'mp123uvw',
+                        str(self.mass_pos_123uvw_check_box.isChecked()))
+        self.config.set('Channels', 'mp456zne',
+                        str(self.mass_pos_456zne_check_box.isChecked()))
         self.config.set('Channels', 'all_waveform_chans',
                         str(self.all_wf_chans_check_box.isChecked()))
         for i, checkbox in enumerate(self.ds_check_boxes, start=1):
diff --git a/sohstationviewer/view/ui/main_ui.py b/sohstationviewer/view/ui/main_ui.py
index 1784637c5..464e1ac75 100755
--- a/sohstationviewer/view/ui/main_ui.py
+++ b/sohstationviewer/view/ui/main_ui.py
@@ -122,15 +122,15 @@ class UIMainWindow(object):
         """
         self.gap_len_line_edit: Union[QLineEdit, None] = None
         """
-        mass_pos_123zne_check_box: QCheckBox: If checked, mass position 1,2,3
+        mass_pos_123uvw_check_box: QCheckBox: If checked, mass position 1,2,3
             will be read
         """
-        self.mass_pos_123zne_check_box: Union[QCheckBox, None] = None
+        self.mass_pos_123uvw_check_box: Union[QCheckBox, None] = None
         """
-        mass_pos_456uvw_check_box: If checked, mass position 1,2,3
+        mass_pos_456zne_check_box: If checked, mass position 1,2,3
             will be read.
         """
-        self.mass_pos_456uvw_check_box: Union[QCheckBox, None] = None
+        self.mass_pos_456zne_check_box: Union[QCheckBox, None] = None
 
         """
         all_wf_chans_check_box: For user to select to read all
@@ -526,14 +526,13 @@ class UIMainWindow(object):
 
         mass_pos_layout = QHBoxLayout()
         left_layout.addLayout(mass_pos_layout)
-        mass_pos_layout.addWidget(QLabel('MassPos '))
-        self.mass_pos_123zne_check_box = QCheckBox(
-            '123/ZNE/ABC', self.central_widget)
-        self.mass_pos_123zne_check_box.setMinimumWidth(120)
-        mass_pos_layout.addWidget(self.mass_pos_123zne_check_box)
-        self.mass_pos_456uvw_check_box = QCheckBox(
-            '456/UVW', self.central_widget)
-        mass_pos_layout.addWidget(self.mass_pos_456uvw_check_box)
+        mass_pos_layout.addWidget(QLabel('Mass Pos '))
+        self.mass_pos_123uvw_check_box = QCheckBox(
+            '123/UVW', self.central_widget)
+        mass_pos_layout.addWidget(self.mass_pos_123uvw_check_box)
+        self.mass_pos_456zne_check_box = QCheckBox(
+            '456/ZNE', self.central_widget)
+        mass_pos_layout.addWidget(self.mass_pos_456zne_check_box)
 
         add_separation_line(left_layout)
 
diff --git a/sohstationviewer/view/util/functions.py b/sohstationviewer/view/util/functions.py
index 4e56d329a..5ea169630 100644
--- a/sohstationviewer/view/util/functions.py
+++ b/sohstationviewer/view/util/functions.py
@@ -205,11 +205,40 @@ def check_chan_wildcards_format(wildcards: str):
                 )
 
 
+def _check_included_channel_group(
+        groups: List[List[str]],
+        included_mp: List[str]) -> (List[str], List[str]):
+    """
+    If any channel in included_mp matches a channel from one of the
+    lists in groups, the entire list will be used for checking
+    missing channels.
+    If no channel in included_mp matches any of the channels from these
+    lists, the group's name (e.g. 123/UVW) will be reported as not found.
+    :param groups: different groups of mass pos channels to be checked against
+    :param included_mp: mass pos channels included in the data set
+    :return:
+        + requested_channels: channels to be checked against
+        + not_found_channels: list of channels to be reported as not found
+         which is empty list or list of the group's namen
+    """
+    requested_channels = []
+    not_found_channels = []
+    for group in groups:
+        if any(chan in group for chan in included_mp):
+            requested_channels += group
+    if not requested_channels:
+        group_str_list = []
+        for group in groups:
+            group_str_list.append("".join(group))
+        not_found_channels = ["/".join(group_str_list)]
+    return requested_channels, not_found_channels
+
+
 def check_masspos(mp_data: Dict[str, Dict],
                   selected_data_set_id: Union[tuple, str],
                   include_mp123: bool, include_mp456: bool) -> None:
     """
-    Check mass positions channels to raise Exception if requested channels
+    Check mass position channels to raise Exception if requested channels
         aren't included.
     :param mp_data: mass position channels of the selected selected_data_set_id
     :param selected_data_set_id: selected data set id to be used in the
@@ -221,62 +250,27 @@ def check_masspos(mp_data: Dict[str, Dict],
     for chan_id in mp_data.keys():
         included_mp.append(chan_id[-1])
     req_mp = []
-
+    not_found_mp = []
     if include_mp123:
-        """
-        If the MassPos 123/ZNE/ABC checkbox is checked, the included_mp list
-        will be compared against 3 predefined lists: 123, ZNE, ABC.
-        If any channel in included_mp matches a channel from one of these
-        lists, the entire list (123, ZNE, or ABC) will be used for checking
-        missing channels.
-        If no channel in included_mp matches any of the channels from these
-        lists, only the 123 list will be included in the warning message
-        regarding missing channels.
-        """
-        req_mp123 = []              # represent 123/zne/abc
-        mp123 = ['1', '2', '3']
-        mpzne = ['Z', 'N', 'E']
-        mpabc = ['A', 'B', 'C']
-        if any(chan in mp123 for chan in included_mp):
-            req_mp123 += mp123
-        if any(chan in mpzne for chan in included_mp):
-            req_mp123 += mpzne
-        if any(chan in mpabc for chan in included_mp):
-            req_mp123 += mpabc
-
-        if req_mp123 == []:
-            # if none of 123/zne/abc, just report 123
-            req_mp123 += mp123
+        req_mp123, not_found_mp123 = _check_included_channel_group(
+            [['1', '2', '3'], ['U', 'V', 'W']], included_mp)
+        not_found_mp += not_found_mp123
         req_mp += req_mp123
 
     if include_mp456:
-        """
-        If the MassPos 456/UVW checkbox is checked, the included_mp list
-        will be compared against 2 predefined lists: 456, UVW.
-        If any channel in included_mp matches a channel from one of these
-        lists, the entire list (123, ZNE, or ABC) will be used for checking
-        missing channels.
-        If no channel in included_mp matches any of the channels from these
-        lists, only the 456 list will be included in the warning message
-        regarding missing channels.
-        """
-        req_mp456 = []              # represent 456/uvw
-        mp456 = ['4', '5', '6']
-        mpuvw = ['U', 'V', 'W']
-        if any(chan in mp456 for chan in included_mp):
-            req_mp456 += mp456
-        if any(chan in mpuvw for chan in included_mp):
-            req_mp456 += mpuvw
-        if req_mp456 == []:
-            # if none of 456/uvw, just report 456
-            req_mp456 += mp456
+        req_mp456, not_found_mp456 = _check_included_channel_group(
+            [['4', '5', '6'], ['Z', 'N', 'E']], included_mp)
+        not_found_mp += not_found_mp456
         req_mp += req_mp456
 
-    not_included_mp = [mp for mp in req_mp if mp not in included_mp]
+    not_found_mp = \
+        [mp for mp in req_mp if mp not in included_mp] + not_found_mp
 
-    if not_included_mp != []:
-        raise Exception(f"Data set {selected_data_set_id} doesn't include mass"
-                        f" position {','.join(not_included_mp)}")
+    if not_found_mp:
+        not_included_mp_str = ', '.join(not_found_mp)
+        raise Exception(f"Did not find mass-position channel(s) "
+                        f"{not_included_mp_str} in data set "
+                        f"{selected_data_set_id}.")
 
 
 def extract_netcodes(data_obj):
-- 
GitLab