Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • software_public/passoft/sohstationviewer
1 result
Show changes
Commits on Source (4)
...@@ -86,3 +86,6 @@ ENV/ ...@@ -86,3 +86,6 @@ ENV/
.idea/ .idea/
.DS_Store .DS_Store
# program config
sohstationviewer/conf/read_settings.ini
...@@ -182,7 +182,9 @@ class MainWindow(QtWidgets.QMainWindow, UIMainWindow): ...@@ -182,7 +182,9 @@ class MainWindow(QtWidgets.QMainWindow, UIMainWindow):
self.is_plotting_tps: bool = False self.is_plotting_tps: bool = False
self.is_stopping: bool = False self.is_stopping: bool = False
self.background_black_radio_button.toggle() self.read_config()
self.validate_config()
self.apply_config()
@QtCore.Slot() @QtCore.Slot()
def open_data_type(self) -> None: def open_data_type(self) -> None:
...@@ -953,6 +955,45 @@ class MainWindow(QtWidgets.QMainWindow, UIMainWindow): ...@@ -953,6 +955,45 @@ class MainWindow(QtWidgets.QMainWindow, UIMainWindow):
for window in QtWidgets.QApplication.topLevelWidgets(): for window in QtWidgets.QApplication.topLevelWidgets():
window.close() window.close()
self.write_config()
def write_config(self):
"""
Write the current state of the program to the config file.
"""
self.config.set('FileRead', 'from_data_card',
str(self.from_data_card_check_box.isChecked()))
self.config.set('FileRead', 'data',
str(self.data_radio_button.isChecked()))
self.config.set('FileRead', 'sdata',
str(self.sdata_radio_button.isChecked()))
self.config.set('ColorMode', 'black',
str(self.background_black_radio_button.isChecked()))
self.config.set('ColorMode', 'white',
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', 'all_waveform_chans',
str(self.all_wf_chans_check_box.isChecked()))
for i, checkbox in enumerate(self.ds_check_boxes, start=1):
self.config.set('Channels', f'ds{i}', str(checkbox.isChecked()))
self.config.set('Channels', 'mseed_wildcard',
self.mseed_wildcard_edit.text())
self.config.set('Channels', 'plot_tps',
str(self.tps_check_box.isChecked()))
self.config.set('Channels', 'plot_raw',
str(self.raw_check_box.isChecked()))
self.config.set('ChannelsPreference', 'all_soh',
str(self.all_soh_chans_check_box.isChecked()))
self.config.set('ChannelsPreference', 'pref_code',
self.curr_pref_soh_list_name_txtbox.text())
with open('sohstationviewer/conf/read_settings.ini', 'w') as file:
self.config.write(file)
def delete_old_temp_data_folder(self) -> None: def delete_old_temp_data_folder(self) -> None:
""" """
Delete temp_data_folder which is used for keeping memmap files in case Delete temp_data_folder which is used for keeping memmap files in case
......
...@@ -195,7 +195,7 @@ def get_gps_channel_prefix(data_obj: MSeed, data_type: str) -> Optional[str]: ...@@ -195,7 +195,7 @@ def get_gps_channel_prefix(data_obj: MSeed, data_type: str) -> Optional[str]:
def get_chan_soh_trace_as_dict(data_obj: MSeed, chan: str def get_chan_soh_trace_as_dict(data_obj: MSeed, chan: str
) -> Dict[float, float]: ) -> Dict[float, List[float]]:
""" """
Get the data of a channel as a dictionary mapping a data point's time to Get the data of a channel as a dictionary mapping a data point's time to
its data. Suppose that gps data's spr <= 1. its data. Suppose that gps data's spr <= 1.
...@@ -204,13 +204,12 @@ def get_chan_soh_trace_as_dict(data_obj: MSeed, chan: str ...@@ -204,13 +204,12 @@ def get_chan_soh_trace_as_dict(data_obj: MSeed, chan: str
:return: a dict that maps the times of channel chan to its data :return: a dict that maps the times of channel chan to its data
""" """
chan_data = data_obj.soh_data[data_obj.selected_key][chan] chan_data = data_obj.soh_data[data_obj.selected_key][chan]
data = chan_data['tracesInfo']['data'] traces = chan_data['tracesInfo']
times = chan_data['tracesInfo']['times'] times = np.hstack([trace['times'] for trace in traces])
# Remove the filled in gaps from the data data = np.hstack([trace['data'] for trace in traces])
data = data[~data.mask] data_dict = {}
times = times[~times.mask] for times_point, data_point in zip(times, data):
data_dict = {time: data data_dict.setdefault(times_point, []).append(data_point)
for time, data in np.column_stack((times, data))}
return data_dict return data_dict
...@@ -252,13 +251,15 @@ def extract_gps_data_pegasus_centaur(data_obj: MSeed, data_type: str ...@@ -252,13 +251,15 @@ def extract_gps_data_pegasus_centaur(data_obj: MSeed, data_type: str
# There is no channel for GPS fix type in Pegasus and Centaur data, # There is no channel for GPS fix type in Pegasus and Centaur data,
# so we are giving it a dummy value. # so we are giving it a dummy value.
fix_type = 'N/A' fix_type = 'N/A'
lat = la_dict.get(time, None) current_lat = la_dict.get(time, None)
long = lo_dict.get(time, None) current_long = lo_dict.get(time, None)
height = el_dict.get(time, None) current_height = el_dict.get(time, None)
# We are ignoring any point that does not have complete location data. # We are ignoring any point that does not have complete location data.
# It might be possible to ignore points with missing latitude or # It might be possible to ignore points with missing latitude or
# longitude, seeing as height is not required to plot a GPS point. # longitude, seeing as height is not required to plot a GPS point.
if lat is None or long is None or height is None: if (current_lat is None or
current_long is None or
current_height is None):
continue continue
# Convert the location data to the appropriate unit. Centaur and # Convert the location data to the appropriate unit. Centaur and
# Pegasus dataloggers both store latitude and longitude in microdegree, # Pegasus dataloggers both store latitude and longitude in microdegree,
...@@ -266,20 +267,26 @@ def extract_gps_data_pegasus_centaur(data_obj: MSeed, data_type: str ...@@ -266,20 +267,26 @@ def extract_gps_data_pegasus_centaur(data_obj: MSeed, data_type: str
# stores elevation in micrometer while Pegasus stores it in centimeter, # stores elevation in micrometer while Pegasus stores it in centimeter,
# so we have to use a difference conversion factor for elevation # so we have to use a difference conversion factor for elevation
# depending on the data type. # depending on the data type.
lat = lat / 1e6 for i, num_sats in enumerate(num_sats_used):
long = long / 1e6 try:
# The GPS prefix is unique between Pegasus (V) and Centaur (G), so we lat = current_lat[i] / 1e6
# can use it in place of the data type. long = current_long[i] / 1e6
if gps_prefix == 'V': # The GPS prefix is unique between Pegasus (V) and Centaur (G),
height_factor = 100 # so we can use it in place of the data type.
else: if gps_prefix == 'V':
height_factor = 1e6 height_factor = 100
height = height / height_factor else:
height_unit = 'M' height_factor = 1e6
formatted_time = UTCDateTime(time).strftime('%Y-%m-%d %H:%M:%S') height = current_height[i] / height_factor
gps_point = GPSPoint(formatted_time, fix_type, num_sats_used, lat, height_unit = 'M'
long, height, height_unit) formatted_time = UTCDateTime(time).strftime(
extracted_gps_points.append(gps_point) '%Y-%m-%d %H:%M:%S'
)
gps_point = GPSPoint(formatted_time, fix_type, num_sats, lat,
long, height, height_unit)
extracted_gps_points.append(gps_point)
except IndexError:
break
# We only need to loop through one dictionary. If a time is not # We only need to loop through one dictionary. If a time is not
# available for a channel, the GPS data point at that time would be # available for a channel, the GPS data point at that time would be
# invalid (it is missing a piece of data). Once we loop through a # invalid (it is missing a piece of data). Once we loop through a
......
...@@ -138,6 +138,10 @@ class SOHWidget(plotting_widget.PlottingWidget): ...@@ -138,6 +138,10 @@ class SOHWidget(plotting_widget.PlottingWidget):
:param first_time: bool - flag identify when the data set is first :param first_time: bool - flag identify when the data set is first
plotted, not zoom in yet plotted, not zoom in yet
""" """
# These channels contain GPS data, which we only show in the GPS
# dialog.
if chan_id in {'GLO', 'VLO', 'VLA', 'GLA', 'VEL', 'VNS', 'GNS', 'GEL'}:
return
chan_db_info = c_data['chan_db_info'] chan_db_info = c_data['chan_db_info']
plot_type = chan_db_info['plotType'] plot_type = chan_db_info['plotType']
if c_data['samplerate'] <= 1: if c_data['samplerate'] <= 1:
......
# UI and connectSignals for main_window # UI and connectSignals for main_window
import configparser
from pathlib import Path
from typing import Union, List, Optional from typing import Union, List, Optional
from PySide2 import QtCore, QtGui, QtWidgets from PySide2 import QtCore, QtGui, QtWidgets
...@@ -263,6 +265,8 @@ class UIMainWindow(object): ...@@ -263,6 +265,8 @@ class UIMainWindow(object):
""" """
self.doc_action: Union[QAction, None] = None self.doc_action: Union[QAction, None] = None
self.config: Union[configparser.ConfigParser, None] = None
def setup_ui(self, main_window): def setup_ui(self, main_window):
""" """
Setting up layout, widgets, menus for main_window Setting up layout, widgets, menus for main_window
...@@ -451,6 +455,8 @@ class UIMainWindow(object): ...@@ -451,6 +455,8 @@ class UIMainWindow(object):
gap_layout.addWidget(self.gap_len_line_edit) gap_layout.addWidget(self.gap_len_line_edit)
gap_layout.addWidget(QLabel(' m')) gap_layout.addWidget(QLabel(' m'))
add_separation_line(left_layout)
mass_pos_layout = QHBoxLayout() mass_pos_layout = QHBoxLayout()
left_layout.addLayout(mass_pos_layout) left_layout.addLayout(mass_pos_layout)
mass_pos_layout.addWidget(QLabel('Mass Pos ')) mass_pos_layout.addWidget(QLabel('Mass Pos '))
...@@ -785,3 +791,141 @@ class UIMainWindow(object): ...@@ -785,3 +791,141 @@ class UIMainWindow(object):
self.read_button.clicked.connect(main_window.read_selected_files) self.read_button.clicked.connect(main_window.read_selected_files)
self.stop_button.clicked.connect(main_window.stop) self.stop_button.clicked.connect(main_window.stop)
def read_config(self):
self.config = configparser.ConfigParser()
config_path = Path('sohstationviewer/conf/read_settings.ini')
if not config_path.exists():
default_config = '''
[FileRead]
from_data_card = False
data = False
sdata = False
[ColorMode]
black = True
white = False
[Gap]
min_gap_length =
[Channels]
mp123zne = False
mp456uvw = False
all_waveform_chans = False
ds1 = False
ds2 = False
ds3 = False
ds4 = False
ds5 = False
ds6 = False
ds7 = False
ds8 = False
mseed_wildcard =
plot_tps = False
plot_raw = False
[ChannelsPreference]
all_soh = True
pref_code =
'''
self.config.read_string(default_config)
else:
self.config.read(config_path)
def validate_config(self):
class BadConfigError(RuntimeError):
"""
Error raised when there is a problem with the configuration
"""
pass
expected_keys = {'from_data_card', 'data', 'sdata',
'black', 'white',
'min_gap_length',
'mp123zne', 'mp456uvw', 'all_waveform_chans', 'ds1',
'ds2', 'ds3', 'ds4', 'ds5', 'ds6', 'ds7', 'ds8',
# noqa
'mseed_wildcard', 'plot_tps', 'plot_raw',
'all_soh', 'pref_code'}
loaded_keys = {item[0]
for (section, _) in self.config.items()
for item in self.config.items(section)}
missing_keys = expected_keys - loaded_keys
if missing_keys:
raise BadConfigError(f'Some required keys are missing. Please'
f'make sure they are in the config file. The '
f'missing keys are: {", ".join(missing_keys)}'
)
data_checked = self.config.getboolean('FileRead', 'data')
sdata_checked = self.config.getboolean('FileRead', 'sdata')
if data_checked and sdata_checked:
raise BadConfigError('data and sdata cannot both be chosen.')
black_color_mode = self.config.getboolean('ColorMode', 'black')
white_color_mode = self.config.getboolean('ColorMode', 'white')
if black_color_mode and white_color_mode:
raise BadConfigError('Cannot be in both color modes at once.')
all_waveform_chans = self.config.getboolean('Channels',
'all_waveform_chans')
data_stream_chosen = any(self.config.getboolean('Channels', f'ds{i}')
for i in range(1, 9)
)
mseed_wildcard = self.config.get('Channels',
'mseed_wildcard')
wildcard_not_empty = (mseed_wildcard != '')
if all_waveform_chans and (data_stream_chosen or wildcard_not_empty):
raise BadConfigError(
'Waveform channel selector can only be in one mode at a time.')
all_soh = self.config.getboolean('ChannelsPreference', 'all_soh')
channel_pref_code = self.config.get('ChannelsPreference', 'pref_code')
if all_soh and (channel_pref_code != ''):
raise BadConfigError('You can only choose one list of channels to '
'be plotted.')
def apply_config(self):
get_bool = self.config.getboolean
self.from_data_card_check_box.setChecked(
get_bool('FileRead', 'from_data_card')
)
self.data_radio_button.setChecked(get_bool('FileRead', 'data'))
self.sdata_radio_button.setChecked(get_bool('FileRead', 'sdata'))
self.background_black_radio_button.setChecked(
get_bool('ColorMode', 'black')
)
self.background_white_radio_button.setChecked(
get_bool('ColorMode', 'white')
)
self.gap_len_line_edit.setText(
self.config.get('Gap', 'min_gap_length')
)
self.mass_pos_123zne_check_box.setChecked(
get_bool('Channels', 'mp123zne')
)
self.mass_pos_456uvw_check_box.setChecked(
get_bool('Channels', 'mp456uvw')
)
self.all_wf_chans_check_box.setChecked(
get_bool('Channels', 'all_waveform_chans')
)
for i, checkbox in enumerate(self.ds_check_boxes, start=1):
checkbox.setChecked(get_bool('Channels', f'ds{i}'))
self.mseed_wildcard_edit.setText(
self.config.get('Channels', 'mseed_wildcard')
)
self.tps_check_box.setChecked(get_bool('Channels', 'plot_tps'))
self.raw_check_box.setChecked(get_bool('Channels', 'plot_raw'))
self.all_soh_chans_check_box.setChecked(
get_bool('ChannelsPreference', 'all_soh')
)
self.curr_pref_soh_list_name_txtbox.setText(
self.config.get('ChannelsPreference', 'pref_code')
)