From d76d628511f19d3f5fb8adbcfd5300350406379e Mon Sep 17 00:00:00 2001
From: spencer <swilbur@usgs.gov>
Date: Fri, 21 Feb 2025 14:07:23 -0700
Subject: [PATCH 1/2] Added NEIC client parameters to FDSNFactory.py

---
 geomagio/edge/FDSNFactory.py | 89 ++++++++++++++++++++++++++----------
 1 file changed, 66 insertions(+), 23 deletions(-)

diff --git a/geomagio/edge/FDSNFactory.py b/geomagio/edge/FDSNFactory.py
index b72228e8..a15f25af 100644
--- a/geomagio/edge/FDSNFactory.py
+++ b/geomagio/edge/FDSNFactory.py
@@ -12,6 +12,7 @@ from obspy import Stream, UTCDateTime, Trace, read, read_inventory
 from obspy.clients.fdsn.header import FDSNNoDataException
 from obspy.clients.iris import Client
 from obspy.clients.fdsn import Client as FDSNClient
+from obspy.clients import neic
 
 from .. import ChannelConverter, TimeseriesUtility
 from ..geomag_types import DataInterval, DataType
@@ -68,13 +69,15 @@ class FDSNFactory(TimeseriesFactory):
 
     Notes
     -----
-    This is designed to read from the FDSN Client, but it
+    This is designed to read from the FDSN Client and EDGE if data is less than 1 day old, but it
         currently only writes to an edge.
     """
 
     def __init__(
         self,
         base_url: str = "http://service.iris.edu",
+        host: Optional[str] = None,
+        port: Optional[int] = None,
         tag: str = "GeomagAlg",
         observatory: Optional[str] = None,
         network: str = "NT",
@@ -90,6 +93,8 @@ class FDSNFactory(TimeseriesFactory):
     ):
         TimeseriesFactory.__init__(self, observatory, channels, type, interval)
         self.Client = FDSNClient(base_url)
+        self.port = port  # Need to add this paramter for later logic
+        self.host = host
         self.remove_sensitivity = remove_sensitivity
         self.tag = tag
         self.interval = interval
@@ -182,7 +187,6 @@ class FDSNFactory(TimeseriesFactory):
         #     # restore stdout
         #     sys.stdout = original_stdout
         self._post_process(timeseries, starttime, endtime, channels)
-
         return timeseries
 
     def _get_timeseries(
@@ -229,36 +233,69 @@ class FDSNFactory(TimeseriesFactory):
             network=self.network,
             location=self.locationCode,
         )
-
         # geomag-algorithms *should* treat starttime/endtime as inclusive everywhere;
         # according to its author, EdgeCWB is inclusive of starttime, but exclusive of
         # endtime, to satisfy seismic standards/requirements, to precision delta/2;
         half_delta = TimeseriesUtility.get_delta_from_interval(interval) / 2
 
-        # Rotate the trace into a right handed coordinate frame.
-        # This will work assuming the metadata is reported correctly.
-
         # Channel that require rotations
         channel_rotations = ["X", "Y", "Z"]
 
         try:
-            data = self.Client.get_waveforms(
+            # Create Inventory Object for rotations and attach response
+            inv = self.Client.get_stations(
                 network=sncl.network,
                 station=sncl.station,
-                location=sncl.location,
-                channel=sncl.channel,
+                location=sncl.location,  # Use location if necessary
+                channel=sncl.channel,  # Request *F1, *F2, and *FZ
                 starttime=starttime,
-                endtime=endtime + half_delta,
-                attach_response=True,
+                endtime=endtime,
+                level="response",
             )
+
+            # Determine whether to use FDSN Client or request data from EDGE
+            if endtime >= (UTCDateTime.now() - 86400):
+                print("The endtime is less than a day later!", endtime)
+
+                self.host = "137.227.252.145"
+                self.port = 2061
+                # use NEIC Client instead
+                neic_client = neic.Client(self.host, self.port)
+
+                neic_channel = [ch.strip() for ch in sncl.channel.split(",")]
+                data = Stream()
+
+                for single_channel in neic_channel:
+                    data += neic_client.get_waveforms(
+                        network=sncl.network,
+                        station=sncl.station,
+                        location=sncl.location,
+                        channel=single_channel,
+                        starttime=starttime,
+                        endtime=endtime + half_delta,
+                    )
+
+                    data.attach_response(inv)
+
+            else:
+
+                data = self.Client.get_waveforms(
+                    network=sncl.network,
+                    station=sncl.station,
+                    location=sncl.location,
+                    channel=sncl.channel,
+                    starttime=starttime,
+                    endtime=endtime + half_delta,
+                    attach_response=True,
+                )
+
             # Perfrom calibrations to properly remove response/sensitivity
             data = self._calibrate_sensitivty_and_response(data)
 
             if channel in channel_rotations:
                 data = self._rotate_and_return_requested_channel(
-                    data, sncl, starttime, endtime, channel
+                    data, sncl, inv, starttime, endtime, channel
                 )
-
         except FDSNNoDataException:
             data = Stream()
             print(
@@ -368,6 +405,7 @@ class FDSNFactory(TimeseriesFactory):
         self,
         data: Stream,
         sncl,
+        inv,
         starttime: UTCDateTime,
         endtime: UTCDateTime,
         requested_channel: str,
@@ -378,19 +416,22 @@ class FDSNFactory(TimeseriesFactory):
         """
 
         # Initialize FDSN client and get the necessary data
-        FDSN = self.Client
+        # FDSN = self.Client
         # Determine if the requested channel is X, Y, or
 
-        inv = FDSN.get_stations(
-            network=sncl.network,
-            station=sncl.station,
-            location=sncl.location,  # Use location if necessary
-            channel=sncl.channel,  # Request *F1, *F2, and *FZ
-            starttime=starttime,
-            endtime=endtime,
-            level="response",
+        # inv = FDSN.get_stations(
+        #     network=sncl.network,
+        #     station=sncl.station,
+        #     location=sncl.location,  # Use location if necessary
+        #     channel=sncl.channel,  # Request *F1, *F2, and *FZ
+        #     starttime=starttime,
+        #     endtime=endtime,
+        #     level="response",
+        # )
+        print(
+            "The SNCL Channel being requested for teh inventory object in rotations: ",
+            sncl.channel,
         )
-
         # Rotate the stream to ZNE
         data.rotate(method="->ZNE", inventory=inv)
 
@@ -445,4 +486,6 @@ class FDSNFactory(TimeseriesFactory):
                         # Geomag Program observatories use 2nd order instrument_polynomial
                         # to capture both offset and scale, but no frequency response
                         data.remove_response()
+            else:
+                print("NO RESPONSE ATTIBUTE WAS DEALT WITH")
         return data
-- 
GitLab


From 3795a9bbd24313c7ec502ce1fb9c01a2e407e700 Mon Sep 17 00:00:00 2001
From: spencer <swilbur@usgs.gov>
Date: Mon, 3 Mar 2025 15:35:03 -0700
Subject: [PATCH 2/2] Addressed Josh's Comments

---
 geomagio/edge/FDSNFactory.py | 44 ++++++++++--------------------------
 1 file changed, 12 insertions(+), 32 deletions(-)

diff --git a/geomagio/edge/FDSNFactory.py b/geomagio/edge/FDSNFactory.py
index a15f25af..4445d98c 100644
--- a/geomagio/edge/FDSNFactory.py
+++ b/geomagio/edge/FDSNFactory.py
@@ -69,15 +69,14 @@ class FDSNFactory(TimeseriesFactory):
 
     Notes
     -----
-    This is designed to read from the FDSN Client and EDGE if data is less than 1 day old, but it
-        currently only writes to an edge.
+    This is designed to read from the FDSN Client and EDGE if data is less than 1 day old
     """
 
     def __init__(
         self,
-        base_url: str = "http://service.iris.edu",
-        host: Optional[str] = None,
-        port: Optional[int] = None,
+        fdsn_url: str = "http://service.iris.edu",
+        host: Optional[str] = "edgecwb.usgs.gov",
+        port: Optional[int] = 2061,
         tag: str = "GeomagAlg",
         observatory: Optional[str] = None,
         network: str = "NT",
@@ -92,7 +91,7 @@ class FDSNFactory(TimeseriesFactory):
         ] = None,  # Accepts "remove_sensitivity" or "remove_response"
     ):
         TimeseriesFactory.__init__(self, observatory, channels, type, interval)
-        self.Client = FDSNClient(base_url)
+        self.Client = FDSNClient(fdsn_url)
         self.port = port  # Need to add this paramter for later logic
         self.host = host
         self.remove_sensitivity = remove_sensitivity
@@ -254,11 +253,11 @@ class FDSNFactory(TimeseriesFactory):
             )
 
             # Determine whether to use FDSN Client or request data from EDGE
-            if endtime >= (UTCDateTime.now() - 86400):
-                print("The endtime is less than a day later!", endtime)
+            oldest_neic_utc = UTCDateTime.now() - (
+                86_400 * 60
+            )  # `date` ensures a midnight boundary
+            if starttime >= oldest_neic_utc:
 
-                self.host = "137.227.252.145"
-                self.port = 2061
                 # use NEIC Client instead
                 neic_client = neic.Client(self.host, self.port)
 
@@ -275,8 +274,6 @@ class FDSNFactory(TimeseriesFactory):
                         endtime=endtime + half_delta,
                     )
 
-                    data.attach_response(inv)
-
             else:
 
                 data = self.Client.get_waveforms(
@@ -286,9 +283,11 @@ class FDSNFactory(TimeseriesFactory):
                     channel=sncl.channel,
                     starttime=starttime,
                     endtime=endtime + half_delta,
-                    attach_response=True,
                 )
 
+            # Attach the channel response. This must be done before the _calibarate_sensitivity_and_response function is called
+            data.attach_response(inv)
+
             # Perfrom calibrations to properly remove response/sensitivity
             data = self._calibrate_sensitivty_and_response(data)
 
@@ -415,23 +414,6 @@ class FDSNFactory(TimeseriesFactory):
         Channels are mapped as follows: 'X' -> '*F2', 'Y' -> '*F1', 'Z' -> '*FZ'.
         """
 
-        # Initialize FDSN client and get the necessary data
-        # FDSN = self.Client
-        # Determine if the requested channel is X, Y, or
-
-        # inv = FDSN.get_stations(
-        #     network=sncl.network,
-        #     station=sncl.station,
-        #     location=sncl.location,  # Use location if necessary
-        #     channel=sncl.channel,  # Request *F1, *F2, and *FZ
-        #     starttime=starttime,
-        #     endtime=endtime,
-        #     level="response",
-        # )
-        print(
-            "The SNCL Channel being requested for teh inventory object in rotations: ",
-            sncl.channel,
-        )
         # Rotate the stream to ZNE
         data.rotate(method="->ZNE", inventory=inv)
 
@@ -486,6 +468,4 @@ class FDSNFactory(TimeseriesFactory):
                         # Geomag Program observatories use 2nd order instrument_polynomial
                         # to capture both offset and scale, but no frequency response
                         data.remove_response()
-            else:
-                print("NO RESPONSE ATTIBUTE WAS DEALT WITH")
         return data
-- 
GitLab