diff --git a/geomagio/edge/EdgeFactory.py b/geomagio/edge/EdgeFactory.py index e0f1091c59cab35a47b07ed2b5150641cf847440..1190dbb167356bed32e28dcb11bc9e4bbbc33c6e 100644 --- a/geomagio/edge/EdgeFactory.py +++ b/geomagio/edge/EdgeFactory.py @@ -22,6 +22,7 @@ from ..TimeseriesFactory import TimeseriesFactory from ..TimeseriesFactoryException import TimeseriesFactoryException from ..ObservatoryMetadata import ObservatoryMetadata from .RawInputClient import RawInputClient +from .LegacySNCL import LegacySNCL class EdgeFactory(TimeseriesFactory): @@ -277,188 +278,6 @@ class EdgeFactory(TimeseriesFactory): trace.data = numpy.ma.masked_invalid(trace.data) return stream - def _get_edge_channel(self, observatory, channel, type, interval): - """get edge channel. - - Parameters - ---------- - observatory : str - observatory code - channel : str - single character channel {H, E, D, Z, F, X, Y, G} or - any appropriate edge channel, ie MSD, MGD, HGD. - type : str - data type {definitive, quasi-definitive, variation} - interval : str - interval length {minute, second} - - Returns - ------- - edge_channel - {MVH, MVE, MVD, MGD etc} - """ - edge_interval_code = self._get_interval_code(interval) - edge_channel = None - - # If form is chan.loc, return chan (left) portion. - # Allows specific chan/loc selection. - if channel.find(".") >= 0: - tmplist = channel.split(".") - return tmplist[0].strip() - - if channel == "D": - edge_channel = edge_interval_code + "VD" - elif channel == "E": - edge_channel = edge_interval_code + "VE" - elif channel == "F": - edge_channel = edge_interval_code + "SF" - elif channel == "H": - edge_channel = edge_interval_code + "VH" - elif channel == "Z": - edge_channel = edge_interval_code + "VZ" - elif channel == "G": - edge_channel = edge_interval_code + "SG" - elif channel == "X": - edge_channel = edge_interval_code + "VX" - elif channel == "Y": - edge_channel = edge_interval_code + "VY" - elif channel == "E-E": - edge_channel = edge_interval_code + "QE" - elif channel == "E-N": - edge_channel = edge_interval_code + "QN" - elif channel == "DIST": - edge_channel = edge_interval_code + "DT" - elif channel == "DST": - edge_channel = edge_interval_code + "GD" - elif channel == "SQ": - edge_channel = edge_interval_code + "SQ" - elif channel == "SV": - edge_channel = edge_interval_code + "SV" - else: - edge_channel = channel - return edge_channel - - def _get_edge_location(self, observatory, channel, type, interval): - """get edge location. - - The edge location code is currently determined by the type - passed in. - - Parameters - ---------- - observatory : str - observatory code - channel : str - single character channel {H, E, D, Z, F} - type : str - data type {definitive, quasi-definitive, variation} - interval : str - interval length {minute, second} - - Returns - ------- - location - returns an edge location code - """ - location = None - - # If form is chan.loc, return loc (right) portion - # Allows specific chan/loc selection. - if channel.find(".") >= 0: - tmplist = channel.split(".") - return tmplist[1].strip() - - if self.locationCode is not None: - location = self.locationCode - else: - if type == "variation" or type == "reported": - location = "R0" - elif type == "adjusted" or type == "provisional": - location = "A0" - elif type == "quasi-definitive": - location = "Q0" - elif type == "definitive": - location = "D0" - elif len(type) == 2: - location = type - return location - - def _get_edge_network(self, observatory, channel, type, interval): - """get edge network code. - - Parameters - ---------- - observatory : str - observatory code - channel : str - single character channel {H, E, D, Z, F} - type : str - data type {definitive, quasi-definitive, variation} - interval : str - interval length {minute, second} - - Returns - ------- - network - always NT - """ - return "NT" - - def _get_edge_station(self, observatory, channel, type, interval): - """get edge station. - - Parameters - ---------- - observatory : str - observatory code - channel : str - single character channel {H, E, D, Z, F} - type : str - data type {definitive, quasi-definitive, variation} - interval : str - interval length {minute, second} - - Returns - ------- - station - the observatory is returned as the station - """ - return observatory - - def _get_interval_code(self, interval): - """get edge interval code. - - Converts the metadata interval string, into an edge single character - edge code. - - Parameters - ---------- - observatory : str - observatory code - channel : str - single character channel {H, E, D, Z, F} - type : str - data type {definitive, quasi-definitive, variation} - interval : str - interval length {minute, second} - - Returns - ------- - interval type - """ - interval_code = None - if interval == "day": - interval_code = "D" - elif interval == "hour": - interval_code = "H" - elif interval == "minute": - interval_code = "M" - elif interval == "second": - interval_code = "S" - else: - raise TimeseriesFactoryException('Unexpected interval "%s"' % interval) - return interval_code - def _get_timeseries(self, starttime, endtime, observatory, channel, type, interval): """get timeseries data for a single channel. @@ -482,13 +301,20 @@ class EdgeFactory(TimeseriesFactory): obspy.core.trace timeseries trace of the requested channel data """ - station = self._get_edge_station(observatory, channel, type, interval) - location = self._get_edge_location(observatory, channel, type, interval) - network = self._get_edge_network(observatory, channel, type, interval) - edge_channel = self._get_edge_channel(observatory, channel, type, interval) + sncl = LegacySNCL.get_sncl( + station=observatory, + data_type=type, + interval=interval, + element=channel, + ) try: data = self.client.get_waveforms( - network, station, location, edge_channel, starttime, endtime + sncl.network, + sncl.station, + sncl.location, + sncl.channel, + starttime, + endtime, ) except TypeError: # get_waveforms() fails if no data is returned from Edge @@ -506,9 +332,9 @@ class EdgeFactory(TimeseriesFactory): channel, type, interval, - network, - station, - location, + sncl.network, + sncl.station, + sncl.location, ) self._set_metadata(data, observatory, channel, type, interval) return data @@ -576,10 +402,12 @@ class EdgeFactory(TimeseriesFactory): ----- RawInputClient seems to only work when sockets are """ - station = self._get_edge_station(observatory, channel, type, interval) - location = self._get_edge_location(observatory, channel, type, interval) - network = self._get_edge_network(observatory, channel, type, interval) - edge_channel = self._get_edge_channel(observatory, channel, type, interval) + sncl = LegacySNCL.get_sncl( + station=observatory, + data_type=type, + interval=interval, + element=channel, + ) now = obspy.core.UTCDateTime(datetime.utcnow()) if ((now - endtime) > 864000) and (self.cwbport > 0): @@ -590,7 +418,13 @@ class EdgeFactory(TimeseriesFactory): port = self.write_port ric = RawInputClient( - self.tag, host, port, station, edge_channel, location, network + self.tag, + host, + port, + sncl.station, + sncl.channel, + sncl.location, + sncl.network, ) stream = self._convert_stream_to_masked(timeseries=timeseries, channel=channel) diff --git a/geomagio/edge/LegacySNCL.py b/geomagio/edge/LegacySNCL.py new file mode 100644 index 0000000000000000000000000000000000000000..a2478b599a145f2e95b1b37f99269b7b8932eb8a --- /dev/null +++ b/geomagio/edge/LegacySNCL.py @@ -0,0 +1,136 @@ +from typing import Optional + +from .SNCL import SNCL, _get_location_start + +ELEMENT_CONVERSIONS = { + # e-field + "E-E": "QE", + "E-N": "QN", + # derived indicies + "SQ": "SQ", + "SV": "SV", + "DIST": "DT", + "DST": "GD", +} +CHANNEL_CONVERSIONS = { + ELEMENT_CONVERSIONS[key]: key for key in ELEMENT_CONVERSIONS.keys() +} + + +class LegacySNCL(SNCL): + @classmethod + def get_sncl( + cls, + data_type: str, + element: str, + interval: str, + station: str, + network: str = "NT", + ) -> "LegacySNCL": + return LegacySNCL( + station=station, + network=network, + channel=get_channel(element=element, interval=interval), + location=get_location(element=element, data_type=data_type), + ) + + @property + def element(self) -> str: + return _check_predefined_element(channel=self.channel) or _get_element( + channel=self.channel, location=self.location + ) + + @property + def interval(self) -> str: + channel_start = self.channel[0] + if channel_start == "S": + return "second" + elif channel_start == "M": + return "minute" + elif channel_start == "H": + return "hour" + elif channel_start == "D": + return "day" + raise ValueError(f"Unexcepted interval code: {channel_start}") + + +def get_channel(element: str, interval: str) -> str: + return _check_predefined_channel(element=element, interval=interval) or ( + _get_channel_start(interval=interval) + _get_channel_end(element=element) + ) + + +def get_location(element: str, data_type: str) -> str: + return _get_location_start(data_type=data_type) + _get_location_end(element=element) + + +def _check_predefined_element(channel: str) -> Optional[str]: + channel_end = channel[1:] + if channel_end in CHANNEL_CONVERSIONS: + return CHANNEL_CONVERSIONS[channel_end] + return None + + +def _get_channel_start(interval: str) -> str: + if interval == "second": + return "S" + elif interval == "minute": + return "M" + elif interval == "hour": + return "H" + elif interval == "day": + return "D" + raise ValueError(f" Unexcepted interval: {interval}") + + +def _get_element(channel: str, location: str) -> str: + """Translates channel/location to element""" + element_start = channel[2] + channel = channel + channel_middle = channel[1] + location_end = location[1] + if channel_middle in ["Q", "E"]: + element_end = "_Volt" + elif channel_middle == "Y": + element_end = "_Bin" + elif channel_middle == "K": + element_end = "_Temp" + elif location_end == "1": + element_end = "_Sat" + else: + element_end = "" + return element_start + element_end + + +def _check_predefined_channel(element: str, interval: str) -> Optional[str]: + if element in ELEMENT_CONVERSIONS: + return _get_channel_start(interval=interval) + ELEMENT_CONVERSIONS[element] + elif len(element) == 3: + return element + # chan.loc format + elif "." in element: + channel = element.split(".")[0] + return channel.strip() + else: + return None + + +def _get_channel_end(element: str) -> str: + channel_middle = "V" + if "_Volt" in element: + channel_middle = "E" + elif "_Bin" in element: + channel_middle = "Y" + elif "_Temp" in element: + channel_middle = "K" + elif element in ["F", "G"]: + channel_middle = "S" + channel_end = element.split("_")[0] + return channel_middle + channel_end + + +def _get_location_end(element: str) -> str: + """Translates element suffix to end of location code""" + if "_Sat" in element: + return "1" + return "0" diff --git a/geomagio/edge/MiniSeedFactory.py b/geomagio/edge/MiniSeedFactory.py index 99c3b9eec31e63a125131b6c0f65933220b13822..ec8b54692693e6f033326274e39deab0d51e719f 100644 --- a/geomagio/edge/MiniSeedFactory.py +++ b/geomagio/edge/MiniSeedFactory.py @@ -23,6 +23,7 @@ from ..TimeseriesFactory import TimeseriesFactory from ..TimeseriesFactoryException import TimeseriesFactoryException from ..ObservatoryMetadata import ObservatoryMetadata from .MiniSeedInputClient import MiniSeedInputClient +from .SNCL import SNCL class MiniSeedFactory(TimeseriesFactory): @@ -295,212 +296,6 @@ class MiniSeedFactory(TimeseriesFactory): trace.data = numpy.ma.masked_invalid(trace.data) return stream - def _get_edge_channel(self, observatory, channel, type, interval): - """get edge channel. - - Parameters - ---------- - observatory : str - observatory code - channel : str - single character channel {H, E, D, Z, F, X, Y, G} or - any appropriate edge channel, ie MSD, MGD, HGD. - type : str - data type {definitive, quasi-definitive, variation} - interval : str - interval length {'day', 'hour', 'minute', 'second', 'tenhertz'} - - Returns - ------- - edge_channel - {MVH, MVE, MVD, MGD etc} - """ - edge_interval_code = self._get_interval_code(interval) - edge_channel = None - - # If form is chan.loc, return chan (left) portion. - # Allows specific chan/loc selection. - if channel.find(".") >= 0: - tmplist = channel.split(".") - return tmplist[0].strip() - - # see if channel name uses _ for ELEMENT_SUFFIX - element = None - suffix = None - if channel.find("_") >= 0: - element, suffix = channel.split("_") - - # 10Hz should be bin/volt - if interval == "tenhertz": - middle = None - if suffix == "Bin": - middle = "Y" - elif suffix == "Volt": - middle = "E" - elif suffix is not None: - raise TimeseriesFactoryException( - 'bad channel suffix "%s", wanted "Bin" or "Volt"' % suffix - ) - # check for expected channels - if element in ("U", "V", "W") and middle is not None: - return edge_interval_code + middle + element - else: - # unknown, assume advanced user - return channel - - if suffix is not None: - if suffix == "Dist" or suffix == "SQ" or suffix == "SV" or suffix == "DT": - # these suffixes modify location code, but use element channel - channel = element - else: - raise TimeseriesFactoryException( - 'bad channel suffix "%s", wanted "Dist", "SQ", or "SV"' % suffix - ) - if channel in ("D", "F", "G", "H", "U", "V", "W", "X", "Y", "Z"): - # normal elements - edge_channel = edge_interval_code + "F" + channel - elif channel == "E-E": - edge_channel = edge_interval_code + "QE" - elif channel == "E-N": - edge_channel = edge_interval_code + "QN" - elif channel == "Dst4": - edge_channel = edge_interval_code + "X4" - elif channel == "Dst3": - edge_channel = edge_interval_code + "X3" - else: - edge_channel = channel - return edge_channel - - def _get_edge_location(self, observatory, channel, data_type, interval): - """get edge location. - - The edge location code is currently determined by the type - passed in. - - Parameters - ---------- - observatory : str - observatory code - channel : str - single character channel {H, E, D, Z, F} - data_type : str - data type {definitive, quasi-definitive, variation} - interval : str - interval length {'day', 'hour', 'minute', 'second', 'tenhertz'} - - Returns - ------- - location - returns an edge location code - """ - # If form is chan.loc, return loc (right) portion - # Allows specific chan/loc selection. - if channel.find(".") >= 0: - tmplist = channel.split(".") - return tmplist[1].strip() - # factory override - if self.locationCode is not None: - return self.locationCode - # determine prefix - location_prefix = "R" - if data_type == "variation" or data_type == "reported": - location_prefix = "R" - elif data_type == "adjusted" or data_type == "provisional": - location_prefix = "A" - elif data_type == "quasi-definitive": - location_prefix = "Q" - elif data_type == "definitive": - location_prefix = "D" - # determine suffix - location_suffix = "0" - if channel.find("_") >= 0: - _, suffix = channel.split("_") - if suffix == "Dist": - location_suffix = "D" - elif suffix == "SQ": - location_suffix = "Q" - elif suffix == "SV": - location_suffix = "V" - elif suffix == "DT": - location_suffix = "R" - elif suffix not in ("Bin", "Volt"): - raise TimeseriesFactoryException( - 'bad channel suffix "%s", wanted "Dist", "SQ", or "SV"' % suffix - ) - return location_prefix + location_suffix - - def _get_edge_network(self, observatory, channel, type, interval): - """get edge network code. - - Parameters - ---------- - observatory : str - observatory code - channel : str - single character channel {H, E, D, Z, F} - type : str - data type {definitive, quasi-definitive, variation} - interval : str - interval length {'day', 'hour', 'minute', 'second', 'tenhertz'} - - Returns - ------- - network - always NT - """ - return "NT" - - def _get_edge_station(self, observatory, channel, type, interval): - """get edge station. - - Parameters - ---------- - observatory : str - observatory code - channel : str - single character channel {H, E, D, Z, F} - type : str - data type {definitive, quasi-definitive, variation} - interval : str - interval length {'day', 'hour', 'minute', 'second', 'tenhertz'} - - Returns - ------- - station - the observatory is returned as the station - """ - return observatory - - def _get_interval_code(self, interval): - """get edge interval code. - - Converts the metadata interval string, into an edge single character - edge code. - - Parameters - ---------- - interval : str - interval length {'day', 'hour', 'minute', 'second', 'tenhertz'} - - Returns - ------- - interval type - """ - interval_code = None - if interval == "day": - interval_code = "P" - elif interval == "hour": - interval_code = "R" - elif interval == "minute": - interval_code = "U" - elif interval == "second": - interval_code = "L" - elif interval == "tenhertz": - interval_code = "B" - else: - raise TimeseriesFactoryException('Unexpected interval "%s"' % interval) - return interval_code - def _get_timeseries(self, starttime, endtime, observatory, channel, type, interval): """get timeseries data for a single channel. @@ -524,12 +319,11 @@ class MiniSeedFactory(TimeseriesFactory): obspy.core.trace timeseries trace of the requested channel data """ - station = self._get_edge_station(observatory, channel, type, interval) - location = self._get_edge_location(observatory, channel, type, interval) - network = self._get_edge_network(observatory, channel, type, interval) - edge_channel = self._get_edge_channel(observatory, channel, type, interval) + sncl = SNCL.get_sncl( + station=observatory, data_type=type, interval=interval, element=channel + ) data = self.client.get_waveforms( - network, station, location, edge_channel, starttime, endtime + sncl.network, sncl.station, sncl.location, sncl.channel, starttime, endtime ) data.merge() if data.count() == 0: @@ -540,9 +334,9 @@ class MiniSeedFactory(TimeseriesFactory): channel, type, interval, - network, - station, - location, + sncl.network, + sncl.station, + sncl.location, ) self._set_metadata(data, observatory, channel, type, interval) return data @@ -666,15 +460,17 @@ class MiniSeedFactory(TimeseriesFactory): to_write = to_write.split() to_write = TimeseriesUtility.unmask_stream(to_write) # relabel channels from internal to edge conventions - station = self._get_edge_station(observatory, channel, type, interval) - location = self._get_edge_location(observatory, channel, type, interval) - network = self._get_edge_network(observatory, channel, type, interval) - edge_channel = self._get_edge_channel(observatory, channel, type, interval) + sncl = SNCL.get_sncl( + station=observatory, + data_type=type, + interval=interval, + element=channel, + ) for trace in to_write: - trace.stats.station = station - trace.stats.location = location - trace.stats.network = network - trace.stats.channel = edge_channel + trace.stats.station = sncl.station + trace.stats.location = sncl.location + trace.stats.network = sncl.network + trace.stats.channel = sncl.channel # finally, send to edge self.write_client.send(to_write) diff --git a/geomagio/edge/SNCL.py b/geomagio/edge/SNCL.py new file mode 100644 index 0000000000000000000000000000000000000000..de292587c098ea3866784dcc149784be5c37f046 --- /dev/null +++ b/geomagio/edge/SNCL.py @@ -0,0 +1,191 @@ +from typing import Dict, Optional + +from pydantic import BaseModel + +ELEMENT_CONVERSIONS = { + # e-field + "E-E": "QE", + "E-N": "QN", + # derived indicies + "Dst3": "X3", + "Dst4": "X4", +} + +CHANNEL_CONVERSIONS = { + ELEMENT_CONVERSIONS[key]: key for key in ELEMENT_CONVERSIONS.keys() +} + + +class SNCL(BaseModel): + station: str + network: str = "NT" + channel: str + location: str + + @classmethod + def get_sncl( + cls, + data_type: str, + element: str, + interval: str, + station: str, + network: str = "NT", + ) -> "SNCL": + return SNCL( + station=station, + network=network, + channel=get_channel(element=element, interval=interval), + location=get_location(element=element, data_type=data_type), + ) + + def parse_sncl(self) -> Dict: + return { + "station": self.station, + "network": self.network, + "data_type": self.data_type, + "element": self.element, + "interval": self.interval, + } + + @property + def data_type(self) -> str: + """Translates beginning of location code to data type""" + location_start = self.location[0] + if location_start == "R": + return "variation" + elif location_start == "A": + return "adjusted" + elif location_start == "Q": + return "quasi-definitive" + elif location_start == "D": + return "definitive" + raise ValueError(f"Unexpected location start: {location_start}") + + @property + def element(self) -> str: + return _check_predefined_element(channel=self.channel) or _get_element( + channel=self.channel, location=self.location + ) + + @property + def interval(self) -> str: + """Translates beginning of channel to interval""" + channel_start = self.channel[0] + if channel_start == "B": + return "tenhertz" + elif channel_start == "L": + return "second" + elif channel_start == "U": + return "minute" + elif channel_start == "R": + return "hour" + elif channel_start == "P": + return "day" + raise ValueError(f"Unexcepted interval code: {channel_start}") + + +def get_channel(element: str, interval: str) -> str: + return _check_predefined_channel(element=element, interval=interval) or ( + _get_channel_start(interval=interval) + _get_channel_end(element=element) + ) + + +def get_location(element: str, data_type: str) -> str: + return _get_location_start(data_type=data_type) + _get_location_end(element=element) + + +def _check_predefined_element(channel: str) -> Optional[str]: + channel_end = channel[1:] + if channel_end in CHANNEL_CONVERSIONS: + return CHANNEL_CONVERSIONS[channel_end] + return None + + +def _get_channel_start(interval: str) -> str: + if interval == "tenhertz": + return "B" + if interval == "second": + return "L" + elif interval == "minute": + return "U" + elif interval == "hour": + return "R" + elif interval == "day": + return "P" + raise ValueError(f" Unexcepted interval: {interval}") + + +def _get_element(channel: str, location: str) -> str: + """Translates channel/location to element""" + element_start = channel[2] + channel = channel + channel_middle = channel[1] + location_end = location[1] + if channel_middle == "E": + element_end = "_Volt" + elif channel_middle == "Y": + element_end = "_Bin" + elif channel_middle == "K": + element_end = "_Temp" + elif location_end == "1": + element_end = "_Sat" + elif location_end == "D": + element_end = "_Dist" + elif location_end == "Q": + element_end = "_SQ" + elif location_end == "V": + element_end = "_SV" + else: + element_end = "" + return element_start + element_end + + +def _check_predefined_channel(element: str, interval: str) -> Optional[str]: + if element in ELEMENT_CONVERSIONS: + return _get_channel_start(interval=interval) + ELEMENT_CONVERSIONS[element] + elif len(element) == 3: + return element + # chan.loc format + elif "." in element: + channel = element.split(".")[0] + return channel.strip() + else: + return None + + +def _get_channel_end(element: str) -> str: + channel_middle = "F" + if "_Volt" in element: + channel_middle = "E" + elif "_Bin" in element: + channel_middle = "Y" + elif "_Temp" in element: + channel_middle = "K" + channel_end = element.split("_")[0] + return channel_middle + channel_end + + +def _get_location_start(data_type: str) -> str: + """Translates data type to beginning of location code""" + if data_type == "variation": + return "R" + elif data_type == "adjusted": + return "A" + elif data_type == "quasi-definitive": + return "Q" + elif data_type == "definitive": + return "D" + raise ValueError(f"Unexpected data type: {data_type}") + + +def _get_location_end(element: str) -> str: + """Translates element suffix to end of location code""" + if "_Sat" in element: + return "1" + if "_Dist" in element: + return "D" + if "_SQ" in element: + return "Q" + if "_SV" in element: + return "V" + return "0" diff --git a/geomagio/edge/__init__.py b/geomagio/edge/__init__.py index 2a53748a195983a3dc737b87731330584f10c739..45b3d49c3ac29b3dd213d0f83cf1017ea5d95549 100644 --- a/geomagio/edge/__init__.py +++ b/geomagio/edge/__init__.py @@ -7,6 +7,8 @@ from .LocationCode import LocationCode from .MiniSeedFactory import MiniSeedFactory from .MiniSeedInputClient import MiniSeedInputClient from .RawInputClient import RawInputClient +from .SNCL import SNCL +from .LegacySNCL import LegacySNCL __all__ = [ "EdgeFactory", @@ -14,4 +16,6 @@ __all__ = [ "MiniSeedFactory", "MiniSeedInputClient", "RawInputClient", + "LegacySNCL", + "SNCL", ] diff --git a/geomagio/edge/sncl.py b/geomagio/edge/sncl.py deleted file mode 100644 index 5bfa4ff2ee186cd832a70623fd6b8604ceee3aea..0000000000000000000000000000000000000000 --- a/geomagio/edge/sncl.py +++ /dev/null @@ -1,258 +0,0 @@ -"""SNCL utilities. - -Station -Network -Channel -Location -""" - -# components that map directly to channel suffixes -CHANNEL_FROM_COMPONENT = { - # e-field - "E-E": "QY", - "E-N": "QX", - "E-U": "QU", - "E-V": "QV", - # derived indicies - "AE": "XA", - "DST3": "X3", - "DST": "X4", - "K": "XK", -} -# reverse lookup of component from channel -COMPONENT_FROM_CHANNEL = dict((v, k) for (k, v) in CHANNEL_FROM_COMPONENT.iteritems()) - - -class SNCLException(Exception): - pass - - -def get_scnl( - observatory, - component=None, - channel=None, - data_type="variation", - interval="second", - location=None, - network="NT", -): - """Generate a SNCL code from data attributes. - - Parameters - ---------- - observatory : str - observatory code. - component : str - geomag component name. - channel : str - default None. - use a specific channel code, instead of generating. - data_type : str - default 'variation' - 'variation', 'adjusted', 'quasi-definitive', or 'definitive'. - interval: str|float - default 'second' - 'tenhertz', 'second', 'minute', 'hour', 'day', - or equivalent interval in seconds - location : str - default None - use a specific location code, instead of generating. - network : str - default 'NT' - network `observatory` is a part of. - - Raises - ------ - SNCLException : when unable to generate a SNCL - - Returns - ------- - dict : dictionary containing the following keys - 'station' : observatory code - 'network' : network code - 'channel' : channel code - 'location' : location code - """ - # use explicit channel/location if specified - channel = channel or __get_channel(component, interval) - location = location or __get_location(component, data_type) - return { - "station": observatory, - "network": network, - "channel": channel, - "location": location, - } - - -def parse_sncl(sncl): - """Parse a SNCL code into data attributes. - - Parameters - ---------- - sncl : dict - dictionary object with the following keys - 'station' : observatory code - 'network' : network code - 'channel' : channel code - 'location' : location code - - Raises - ------ - SNCLException : when unable to parse a SNCL - - Returns - ------- - dict : dictionary containing the following keys - 'observatory' : observatory code - 'network' : network code - 'component' : geomag component name - 'data_type' : geomag data type (e.g. 'variation') - 'interval' : data interval in seconds (e.g. 1) - """ - network = sncl["network"] - station = sncl["station"] - channel = sncl["channel"] - location = sncl["location"] - return { - "observatory": station, - "network": network, - "component": __parse_component(channel, location), - "data_type": __parse_data_type(location), - "interval": __parse_interval(channel), - } - - -def __get_channel(component, interval): - channel_start = __get_channel_start(interval) - # check for direct component mappings - if component in CHANNEL_FROM_COMPONENT: - channel_end = CHANNEL_FROM_COMPONENT[component] - else: - channel_end = __get_channel_end(component) - return channel_start + channel_end - - -def __get_channel_start(interval): - if interval == "tenhertz" or interval == 0.1: - return "B" - if interval == "second" or interval == 1: - return "L" - if interval == "minute" or interval == 60: - return "U" - if interval == "hour" or interval == 3600: - return "R" - if interval == "day" or interval == 86400: - return "P" - raise SNCLException("Unexpected interval {}".format(interval)) - - -def __get_channel_end(component): - # default to engineering units - channel_middle = "F" - # check for suffix that may override - component_parts = component.split("-") - channel_end = component_parts[0] - if len(component_parts) > 1: - component_suffix = component_parts[1] - if component_suffix == "-Bin": - channel_middle = "Y" - elif component_suffix == "-Temp": - channel_middle = "K" - elif component_suffix == "-Volt": - channel_middle = "E" - else: - raise SNCLException("Unexpected component {}".format(component)) - return channel_middle + channel_end - - -def __get_location(component, data_type): - location_start = __get_location_start(data_type) - location_end = __get_location_end(component) - return location_start + location_end - - -def __get_location_start(data_type): - if data_type == "variation": - return "R" - elif data_type == "adjusted": - return "A" - elif data_type == "quasi-definitive": - return "Q" - elif data_type == "definitive": - return "D" - raise SNCLException("Unexpected data type {}".format(data_type)) - - -def __get_location_end(component): - if component.endswith("-Sat"): - return "1" - if component.endswith("-Dist"): - return "D" - if component.endswith("-SQ"): - return "Q" - if component.endswith("-SV"): - return "V" - return "0" - - -def __parse_component(channel, location): - channel_end = channel[1:] - if channel_end in COMPONENT_FROM_CHANNEL: - return COMPONENT_FROM_CHANNEL[channel_end] - channel_middle = channel[1] - component = channel[2] - component_end = "" - if channel_middle == "E": - component_end = "-Volt" - elif channel_middle == "K": - component_end = "-Temp" - elif channel_middle == "Y": - component_end = "-Bin" - elif channel_middle == "F": - component_end = __parse_component_end(location) - else: - raise SNCLException("Unexpected channel middle {}".format(channel)) - return component + component_end - - -def __parse_component_end(location): - location_end = location[1] - if location_end == "0": - return "" - if location_end == "1": - return "-Sat" - if location_end == "D": - return "-Dist" - if location_end == "Q": - return "-SQ" - if location_end == "V": - return "-SV" - raise SNCLException("Unexpected location end {}".format(location_end)) - - -def __parse_data_type(location): - location_start = location[0] - if location_start == "R": - return "variation" - if location_start == "A": - return "adjusted" - if location_start == "Q": - return "quasi-definitive" - if location_start == "D": - return "definitive" - raise SNCLException("Unexpected location start {}".format(location_start)) - - -def __parse_interval(channel): - channel_start = channel[0] - if channel_start == "B": - return 0.1 - if channel_start == "L": - return 1 - if channel_start == "U": - return 60 - if channel_start == "R": - return 3600 - if channel_start == "P": - return 86400 - raise SNCLException("Unexpected channel {}".format(channel)) diff --git a/test/edge_test/EdgeFactory_test.py b/test/edge_test/EdgeFactory_test.py index fca38a8c8df6fb8cd08a7717ff2114f5e7f5daf3..6b524fb318b3fad25afdee0613255d4551ff8aba 100644 --- a/test/edge_test/EdgeFactory_test.py +++ b/test/edge_test/EdgeFactory_test.py @@ -5,62 +5,6 @@ from geomagio.edge import EdgeFactory from numpy.testing import assert_equal -def test__get_edge_network(): - """edge_test.EdgeFactory_test.test__get_edge_network()""" - # _get_edge_network should always return NT for use by USGS geomag - assert_equal(EdgeFactory()._get_edge_network(" ", " ", " ", " "), "NT") - - -def test__get_edge_station(): - """edge_test.EdgeFactory_test.test__get_edge_station()""" - # _get_edge_station will return the observatory code passed in. - assert_equal(EdgeFactory()._get_edge_station("BOU", " ", " ", " "), "BOU") - - -def test__get_edge_channel(): - """edge_test.EdgeFactory_test.test__get_edge_channel()""" - # Call private function _get_edge_channel, make certain - # it gets back the appropriate 2 character code. - assert_equal(EdgeFactory()._get_edge_channel("", "D", "", "minute"), "MVD") - assert_equal(EdgeFactory()._get_edge_channel("", "E", "", "minute"), "MVE") - assert_equal(EdgeFactory()._get_edge_channel("", "F", "", "minute"), "MSF") - assert_equal(EdgeFactory()._get_edge_channel("", "H", "", "minute"), "MVH") - assert_equal(EdgeFactory()._get_edge_channel("", "DIST", "", "minute"), "MDT") - assert_equal(EdgeFactory()._get_edge_channel("", "DST", "", "minute"), "MGD") - assert_equal(EdgeFactory()._get_edge_channel("", "E-E", "", "minute"), "MQE") - assert_equal(EdgeFactory()._get_edge_channel("", "E-N", "", "minute"), "MQN") - - -def test__get_edge_location(): - """edge_test.EdgeFactory_test.test__get_edge_location()""" - # Call _get_edge_location, make certain it returns the correct edge - # location code. - assert_equal(EdgeFactory()._get_edge_location("", "", "variation", ""), "R0") - assert_equal(EdgeFactory()._get_edge_location("", "", "quasi-definitive", ""), "Q0") - assert_equal(EdgeFactory()._get_edge_location("", "", "definitive", ""), "D0") - - -def test__get_interval_code(): - """edge_test.EdgeFactory_test.test__get_interval_code()""" - assert_equal(EdgeFactory()._get_interval_code("day"), "D") - assert_equal(EdgeFactory()._get_interval_code("hour"), "H") - assert_equal(EdgeFactory()._get_interval_code("minute"), "M") - assert_equal(EdgeFactory()._get_interval_code("second"), "S") - - -def test__set_metadata(): - """edge_test.EdgeFactory_test.test__set_metadata()""" - # Call _set_metadata with 2 traces, and make certain the stats get - # set for both traces. - trace1 = Trace() - trace2 = Trace() - stream = Stream(traces=[trace1, trace2]) - EdgeFactory()._set_metadata(stream, "BOU", "H", "variation", "minute") - assert_equal(stream[0].stats["channel"], "H") - assert_equal(stream[1].stats["channel"], "H") - - -# def test_get_timeseries(): def dont_get_timeseries(): """edge_test.EdgeFactory_test.test_get_timeseries()""" # Call get_timeseries, and test stats for comfirmation that it came back. diff --git a/test/edge_test/LegacySNCL_test.py b/test/edge_test/LegacySNCL_test.py new file mode 100644 index 0000000000000000000000000000000000000000..adbcc8a8388ba85a543cd075d2e3bccc2c0e5202 --- /dev/null +++ b/test/edge_test/LegacySNCL_test.py @@ -0,0 +1,193 @@ +from geomagio.edge.LegacySNCL import LegacySNCL, get_channel, get_location + + +def test_data_type(): + """edge_test.LegacySNCL_test.test_data_type()""" + assert ( + LegacySNCL(station="BOU", channel="LFU", location="R0").data_type == "variation" + ) + assert ( + LegacySNCL(station="BOU", channel="LFU", location="A0").data_type == "adjusted" + ) + assert ( + LegacySNCL(station="BOU", channel="LFU", location="Q0").data_type + == "quasi-definitive" + ) + assert ( + LegacySNCL(station="BOU", channel="LFU", location="D0").data_type + == "definitive" + ) + + +def test_element(): + """edge_test.LegacySNCL_test.test_element()""" + assert ( + LegacySNCL( + station="BOU", + channel="MVD", + location="R0", + ).element + == "D" + ) + assert ( + LegacySNCL( + station="BOU", + channel="MVU", + location="R0", + ).element + == "U" + ) + assert ( + LegacySNCL( + station="BOU", + channel="MSF", + location="R0", + ).element + == "F" + ) + assert ( + LegacySNCL( + station="BOU", + channel="MVH", + location="R0", + ).element + == "H" + ) + assert ( + LegacySNCL( + station="BOU", + channel="MQE", + location="R0", + ).element + == "E-E" + ) + assert ( + LegacySNCL( + station="BOU", + channel="MQN", + location="R0", + ).element + == "E-N" + ) + assert ( + LegacySNCL( + station="BOU", + channel="MEH", + location="R0", + ).element + == "H_Volt" + ) + assert ( + LegacySNCL( + station="BOU", + channel="MYH", + location="R0", + ).element + == "H_Bin" + ) + assert ( + LegacySNCL( + station="BOU", + channel="MVH", + location="R1", + ).element + == "H_Sat" + ) + assert ( + LegacySNCL( + station="BOU", + channel="MDT", + location="R0", + ).element + == "DIST" + ) + assert ( + LegacySNCL( + station="BOU", + channel="MGD", + location="R0", + ).element + == "DST" + ) + + +def test_get_channel(): + """edge_test.LegacySNCL_test.test_get_channel()""" + assert get_channel(element="D", interval="second") == "SVD" + assert get_channel(element="F", interval="minute") == "MSF" + assert get_channel(element="H", interval="hour") == "HVH" + assert get_channel(element="E-E", interval="day") == "DQE" + assert get_channel(element="E-N", interval="minute") == "MQN" + assert get_channel(element="SQ", interval="minute") == "MSQ" + assert get_channel(element="SV", interval="minute") == "MSV" + assert get_channel(element="UK1", interval="minute") == "UK1" + assert get_channel(element="DIST", interval="minute") == "MDT" + assert get_channel(element="DST", interval="minute") == "MGD" + assert get_channel(element="UK1.R0", interval="minute") == "UK1" + + +def test_get_location(): + """edge_test.LegacySNCL_test.test_get_location()""" + assert get_location(element="D", data_type="variation") == "R0" + assert get_location(element="D", data_type="adjusted") == "A0" + assert get_location(element="D", data_type="quasi-definitive") == "Q0" + assert get_location(element="D", data_type="definitive") == "D0" + assert get_location(element="D_Sat", data_type="variation") == "R1" + + +def test_get_sncl(): + """edge_test.LegacySNCL_test.test_get_sncl()""" + assert LegacySNCL.get_sncl( + station="BOU", data_type="variation", interval="second", element="H" + ) == LegacySNCL(station="BOU", network="NT", channel="SVH", location="R0") + + +def test_interval(): + """edge_test.LegacySNCL_test.test_interval()""" + assert ( + LegacySNCL( + station="BOU", + channel="SVH", + location="R0", + data_format="legacy", + ).interval + == "second" + ) + assert ( + LegacySNCL( + station="BOU", + channel="MVH", + location="R0", + data_format="legacy", + ).interval + == "minute" + ) + assert ( + LegacySNCL( + station="BOU", + channel="HVH", + location="R0", + data_format="legacy", + ).interval + == "hour" + ) + assert ( + LegacySNCL( + station="BOU", + channel="DVH", + location="R0", + data_format="legacy", + ).interval + == "day" + ) + + +def test_parse_sncl(): + """edge_test.LegacySNCL_test.test_parse_sncl()""" + assert LegacySNCL(station="BOU", channel="MVH", location="R0").parse_sncl() == { + "station": "BOU", + "network": "NT", + "data_type": "variation", + "element": "H", + "interval": "minute", + } diff --git a/test/edge_test/MiniSeedFactory_test.py b/test/edge_test/MiniSeedFactory_test.py index a0c57869e7ff0ffcea37f7c6f5c98842347e2492..bd1367d37ad3487522ddad7f5df879c3c37ad5f5 100644 --- a/test/edge_test/MiniSeedFactory_test.py +++ b/test/edge_test/MiniSeedFactory_test.py @@ -9,54 +9,6 @@ from geomagio import TimeseriesUtility from geomagio.edge import MiniSeedFactory, MiniSeedInputClient -def test__get_edge_network(): - """edge_test.MiniSeedFactory_test.test__get_edge_network()""" - # _get_edge_network should always return NT for use by USGS geomag - assert_equal(MiniSeedFactory()._get_edge_network(" ", " ", " ", " "), "NT") - - -def test__get_edge_station(): - """edge_test.MiniSeedFactory_test.test__get_edge_station()""" - # _get_edge_station will return the observatory code passed in. - assert_equal(MiniSeedFactory()._get_edge_station("BOU", " ", " ", " "), "BOU") - - -def test__get_edge_channel(): - """edge_test.MiniSeedFactory_test.test__get_edge_channel()""" - # Call private function _get_edge_channel, make certain - # it gets back the appropriate 2 character code. - factory = MiniSeedFactory() - assert_equal(factory._get_edge_channel("", "D", "", "minute"), "UFD") - assert_equal(factory._get_edge_channel("", "U", "", "minute"), "UFU") - assert_equal(factory._get_edge_channel("", "F", "", "minute"), "UFF") - assert_equal(factory._get_edge_channel("", "H", "", "minute"), "UFH") - assert_equal(factory._get_edge_channel("", "BEU", "", "minute"), "BEU") - assert_equal(factory._get_edge_channel("", "Dst4", "", "minute"), "UX4") - assert_equal(factory._get_edge_channel("", "Dst3", "", "minute"), "UX3") - assert_equal(factory._get_edge_channel("", "E-E", "", "minute"), "UQE") - assert_equal(factory._get_edge_channel("", "E-N", "", "minute"), "UQN") - - -def test__get_edge_location(): - """edge_test.MiniSeedFactory_test.test__get_edge_location()""" - # Call _get_edge_location, make certain it returns the correct edge - # location code. - assert_equal(MiniSeedFactory()._get_edge_location("", "", "variation", ""), "R0") - assert_equal( - MiniSeedFactory()._get_edge_location("", "", "quasi-definitive", ""), "Q0" - ) - assert_equal(MiniSeedFactory()._get_edge_location("", "", "definitive", ""), "D0") - - -def test__get_interval_code(): - """edge_test.MiniSeedFactory_test.test__get_interval_code()""" - assert_equal(MiniSeedFactory()._get_interval_code("day"), "P") - assert_equal(MiniSeedFactory()._get_interval_code("hour"), "R") - assert_equal(MiniSeedFactory()._get_interval_code("minute"), "U") - assert_equal(MiniSeedFactory()._get_interval_code("second"), "L") - assert_equal(MiniSeedFactory()._get_interval_code("tenhertz"), "B") - - class MockMiniSeedInputClient(object): def __init__(self): self.close_called = False @@ -167,7 +119,7 @@ def __create_trace( channel="H", location="R0", data_interval="second", - data_type="interval", + data_type="variation", ): """ Utility to create a trace containing the given numpy array. diff --git a/test/edge_test/SNCL_test.py b/test/edge_test/SNCL_test.py new file mode 100644 index 0000000000000000000000000000000000000000..ea92aa524602cff70dd8688cdd2ed14ebf758c04 --- /dev/null +++ b/test/edge_test/SNCL_test.py @@ -0,0 +1,196 @@ +from geomagio.edge.SNCL import SNCL, get_channel, get_location + + +def test_data_type(): + """edge_test.SNCL_test.test_data_type()""" + assert SNCL(station="BOU", channel="LFU", location="R0").data_type == "variation" + assert SNCL(station="BOU", channel="LFU", location="A0").data_type == "adjusted" + assert ( + SNCL(station="BOU", channel="LFU", location="Q0").data_type + == "quasi-definitive" + ) + assert SNCL(station="BOU", channel="LFU", location="D0").data_type == "definitive" + + +def test_element(): + """edge_test.SNCL_test.test_element()""" + assert ( + SNCL( + station="BOU", + channel="UFD", + location="R0", + ).element + == "D" + ) + assert ( + SNCL( + station="BOU", + channel="UFU", + location="R0", + ).element + == "U" + ) + assert ( + SNCL( + station="BOU", + channel="UFF", + location="R0", + ).element + == "F" + ) + assert ( + SNCL( + station="BOU", + channel="UFH", + location="R0", + ).element + == "H" + ) + assert ( + SNCL( + station="BOU", + channel="UX4", + location="R0", + ).element + == "Dst4" + ) + assert ( + SNCL( + station="BOU", + channel="UX3", + location="R0", + ).element + == "Dst3" + ) + assert ( + SNCL( + station="BOU", + channel="UQE", + location="R0", + ).element + == "E-E" + ) + assert ( + SNCL( + station="BOU", + channel="UQN", + location="R0", + ).element + == "E-N" + ) + assert ( + SNCL( + station="BOU", + channel="BEU", + location="R0", + ).element + == "U_Volt" + ) + assert ( + SNCL( + station="BOU", + channel="BYU", + location="R0", + ).element + == "U_Bin" + ) + assert ( + SNCL( + station="BOU", + channel="UFU", + location="R1", + ).element + == "U_Sat" + ) + + +def test_get_channel(): + """edge_test.SNCL_test.test_get_channel()""" + assert get_channel(element="U_Volt", interval="tenhertz") == "BEU" + assert get_channel(element="U_Bin", interval="tenhertz") == "BYU" + assert get_channel(element="D", interval="second") == "LFD" + assert get_channel(element="F", interval="minute") == "UFF" + assert get_channel(element="H", interval="hour") == "RFH" + assert get_channel(element="Dst4", interval="day") == "PX4" + assert get_channel(element="Dst3", interval="minute") == "UX3" + assert get_channel(element="E-E", interval="minute") == "UQE" + assert get_channel(element="E-N", interval="minute") == "UQN" + assert get_channel(element="UK1", interval="minute") == "UK1" + assert get_channel(element="U_Dist", interval="minute") == "UFU" + assert get_channel(element="U_SQ", interval="minute") == "UFU" + assert get_channel(element="U_SV", interval="minute") == "UFU" + assert get_channel(element="UK1.R0", interval="minute") == "UK1" + + +def test_get_location(): + """edge_test.SNCL_test.test_get_location()""" + assert get_location(element="D", data_type="variation") == "R0" + assert get_location(element="D", data_type="adjusted") == "A0" + assert get_location(element="D", data_type="quasi-definitive") == "Q0" + assert get_location(element="D", data_type="definitive") == "D0" + assert get_location(element="D_Sat", data_type="variation") == "R1" + assert get_location(element="D_Dist", data_type="variation") == "RD" + assert get_location(element="D_SQ", data_type="variation") == "RQ" + assert get_location(element="D_SV", data_type="variation") == "RV" + + +def test_get_sncl(): + """edge_test.SNCL_test.test_get_sncl()""" + assert SNCL.get_sncl( + station="BOU", data_type="variation", interval="second", element="U" + ) == SNCL(station="BOU", network="NT", channel="LFU", location="R0") + + +def test_interval(): + """edge_test.SNCL_test.test_interval()""" + assert ( + SNCL( + station="BOU", + channel="BEU", + location="R0", + ).interval + == "tenhertz" + ) + assert ( + SNCL( + station="BOU", + channel="LEU", + location="R0", + ).interval + == "second" + ) + assert ( + SNCL( + station="BOU", + channel="UEU", + location="R0", + ).interval + == "minute" + ) + assert ( + SNCL( + station="BOU", + channel="REU", + location="R0", + ).interval + == "hour" + ) + assert ( + SNCL( + station="BOU", + channel="PEU", + location="R0", + ).interval + == "day" + ) + + +def test_parse_sncl(): + """edge_test.SNCL_test.test_parse_sncl()""" + assert SNCL(station="BOU", channel="UFU", location="R0").parse_sncl() == { + "station": "BOU", + "network": "NT", + "data_type": "variation", + "element": "U", + "interval": "minute", + }