diff --git a/etc/adjusted/BOU201601vmin.min b/etc/adjusted/BOU201601vmin.min
index 57c20c44233ae76dfd9dae307ce91c14086b4651..83ef32f4830f78ffc089e167dc6230832db4e2ee 100644
--- a/etc/adjusted/BOU201601vmin.min
+++ b/etc/adjusted/BOU201601vmin.min
@@ -5,7 +5,7 @@
  Geodetic Latitude      40.137                                       |
  Geodetic Longitude     254.764                                      |
  Elevation              1682                                         |
- Reported               HEZF                                         |
+ Reported               UVWF                                         |
  Sensor Orientation     HDZF                                         |
  Digital Sampling       100.0 second                                 |
  Data Interval Type     filtered 1-minute (00:15-01:45)              |
@@ -19,7 +19,7 @@
  # CONDITIONS OF USE: The Conditions of Use for data provided        |
  # through INTERMAGNET and acknowledgement templates can be found at |
  # www.intermagnet.org                                               |
-DATE       TIME         DOY     BOUH      BOUE      BOUZ      BOUF   |
+DATE       TIME         DOY     BOUU      BOUV      BOUW      BOUF   |
 2016-01-01 00:00:00.000 001     20735.93    -99.77  47370.21  52248.63
 2016-01-01 00:01:00.000 001     20735.48    -95.30  47370.53  52248.72
 2016-01-01 00:02:00.000 001     20736.30    -91.25  47371.05  52249.49
diff --git a/etc/adjusted/BOU202005adj.min b/etc/adjusted/BOU202005adj.min
index 55f8fcc64580668ad815ae8706c1628cbbea0285..551fd3ed7ec4fabb3ecf4b5a360b782ce4f45b3c 100644
--- a/etc/adjusted/BOU202005adj.min
+++ b/etc/adjusted/BOU202005adj.min
@@ -5,7 +5,7 @@
  Geodetic Latitude      40.137                                       |
  Geodetic Longitude     254.763                                      |
  Elevation              1682                                         |
- Reported               HENULNUL                                     |
+ Reported               UVNULNUL                                     |
  Sensor Orientation     HDZF                                         |
  Digital Sampling       0.01 second                                  |
  Data Interval Type     filtered 1-minute (00:15-01:45)              |
@@ -17,7 +17,7 @@
  # CONDITIONS OF USE: The Conditions of Use for data provided        |
  # through INTERMAGNET and acknowledgement templates can be found at |
  # www.intermagnet.org                                               |
-DATE       TIME         DOY     BOUH      BOUE      BOUNUL    BOUNUL |
+DATE       TIME         DOY     BOUU      BOUV      BOUNUL    BOUNUL |
 2020-05-20 16:15:00.000 141    -20799.04     80.92  99999.00  99999.00
 2020-05-20 16:16:00.000 141    -20798.26     79.97  99999.00  99999.00
 2020-05-20 16:17:00.000 141    -20799.43     81.22  99999.00  99999.00
diff --git a/etc/adjusted/BOU202005vmin.min b/etc/adjusted/BOU202005vmin.min
index 545671c9b844f2f36fa64467d195b52c2780c762..f3d0f15bc47bdb0d77d374942c155d20e95955ee 100644
--- a/etc/adjusted/BOU202005vmin.min
+++ b/etc/adjusted/BOU202005vmin.min
@@ -5,7 +5,7 @@
  Geodetic Latitude      40.137                                       |
  Geodetic Longitude     254.763                                      |
  Elevation              1682                                         |
- Reported               HENULNUL                                     |
+ Reported               UVNULNUL                                     |
  Sensor Orientation     HDZF                                         |
  Digital Sampling       0.01 second                                  |
  Data Interval Type     filtered 1-minute (00:15-01:45)              |
@@ -19,7 +19,7 @@
  # CONDITIONS OF USE: The Conditions of Use for data provided        |
  # through INTERMAGNET and acknowledgement templates can be found at |
  # www.intermagnet.org                                               |
-DATE       TIME         DOY     BOUH      BOUE      BOUNUL    BOUNUL |
+DATE       TIME         DOY     BOUU      BOUV      BOUNUL    BOUNUL |
 2020-05-20 16:15:00.000 141     20799.04    -80.92  99999.00  99999.00
 2020-05-20 16:16:00.000 141     20798.26    -79.97  99999.00  99999.00
 2020-05-20 16:17:00.000 141     20799.43    -81.22  99999.00  99999.00
diff --git a/geomagio/ChannelConverter.py b/geomagio/ChannelConverter.py
index a7c3bfc8b255f77521bce41916ab0b7484a02693..3884af3d286050fba482a6eee352432d87c94c9f 100644
--- a/geomagio/ChannelConverter.py
+++ b/geomagio/ChannelConverter.py
@@ -5,7 +5,7 @@ the geomagnetic community.
 We use three coordinate systems.
 Geo: Based on Geographic North.  X, Y, Z
      X is north, Y is east
-Obs: Based on the observatories orientaion. H, E, Z [d]
+Obs: Based on the observatories orientaion. U, V, W [d]
 Mag: Based on Magnetic North. H, D, Z [E]
 
 d0: Declination baseline in radians
@@ -29,15 +29,15 @@ R2M = 180.0 / numpy.pi * 60  # Radians to Minutes
 # ###
 
 
-def get_geo_from_obs(h, e, d0=0):
+def get_geo_from_obs(u, v, d0=0):
     """gets the geographical components given the observatory components.
 
     Parameters
     __________
-    h: array_like
-        the h component from the observatory
-    e: array_like
-        the e component from the observatory
+    u: array_like
+        the u component from the observatory
+    v: array_like
+        the v component from the observatory
     d0: float
         the declination baseline angle in radians
 
@@ -47,7 +47,7 @@ def get_geo_from_obs(h, e, d0=0):
         [0]: x component as a float
         [1]: y component as a float
     """
-    mag_h, mag_d = get_mag_from_obs(h, e, d0)
+    mag_h, mag_d = get_mag_from_obs(u, v, d0)
     return get_geo_from_mag(mag_h, mag_d)
 
 
@@ -112,15 +112,15 @@ def get_geo_y_from_mag(h, d):
 # ###
 # get magnetic north coordinates from....
 # ###
-def get_mag_from_obs(h, e, d0=0):
+def get_mag_from_obs(u, v, d0=0):
     """gets the magnetic north components given the observatory components.
 
     Parameters
     __________
-    h: array_like
-        the h component from the observatory
-    e: array_like
-        the e component from the observatory
+    u: array_like
+        the u component from the observatory
+    v: array_like
+        the v component from the observatory
     d0: float
         the declination baseline angle in radians
 
@@ -130,8 +130,8 @@ def get_mag_from_obs(h, e, d0=0):
         [0]: total h component as a float
         [1]: total d declination as a float
     """
-    mag_h = get_mag_h_from_obs(h, e)
-    mag_d = get_mag_d_from_obs(h, e, d0)
+    mag_h = get_mag_h_from_obs(u, v)
+    mag_d = get_mag_d_from_obs(u, v, d0)
     return (mag_h, mag_d)
 
 
@@ -156,15 +156,15 @@ def get_mag_from_geo(x, y):
     return (mag_h, mag_d)
 
 
-def get_mag_d_from_obs(h, e, d0=0):
+def get_mag_d_from_obs(u, v, d0=0):
     """gets the magnetic d component given the observatory components.
 
     Parameters
     __________
-    h: array_like
-        the h component from the observatory
-    e: array_like
-        the e component from the observatory
+    u: array_like
+        the u component from the observatory
+    v: array_like
+        the v component from the observatory
     d0: float
         the declination baseline angle in radians
 
@@ -173,7 +173,7 @@ def get_mag_d_from_obs(h, e, d0=0):
     array_like
         the total magnetic declination
     """
-    return numpy.add(d0, get_obs_d_from_obs(h, e))
+    return numpy.add(d0, get_obs_d_from_obs(u, v))
 
 
 def get_mag_d_from_geo(x, y):
@@ -194,22 +194,22 @@ def get_mag_d_from_geo(x, y):
     return numpy.arctan2(y, x)
 
 
-def get_mag_h_from_obs(h, e):
+def get_mag_h_from_obs(u, v):
     """gets the magnetic h component given the observatory components.
 
     Parameters
     __________
-    h: array_like
-        the h component from the observatory
-    e: array_like
-        the e component from the observatory
+    u: array_like
+        the u component from the observatory
+    v: array_like
+        the v component from the observatory
 
     Returns
     _______
     array_like
         the total magnetic h component
     """
-    return numpy.hypot(h, e)
+    return numpy.hypot(u, v)
 
 
 def get_mag_h_from_geo(x, y):
@@ -271,32 +271,32 @@ def get_obs_from_mag(h, d, d0=0):
      Returns
     _______
     tuple of array_like
-        [0]: observatory h component
-        [1]: observatory e component
+        [0]: observatory u component
+        [1]: observatory v component
         [2]: observatory d declination
     """
-    obs_h = get_obs_h_from_mag(h, d, d0)
-    obs_e = get_obs_e_from_mag(h, d, d0)
-    return (obs_h, obs_e)
+    obs_u = get_obs_u_from_mag(h, d, d0)
+    obs_v = get_obs_v_from_mag(h, d, d0)
+    return (obs_u, obs_v)
 
 
 # inividual get obs from calls
-def get_obs_d_from_obs(h, e):
+def get_obs_d_from_obs(u, v):
     """gets the observatory d declination given the observatory components.
 
     Parameters
     __________
-    h: array_like
-        the h component from the observatory
-    e: array_like
-        the e component from the observatory
+    u: array_like
+        the u component from the observatory
+    v: array_like
+        the v component from the observatory
 
     Returns
     _______
     array_like
         the observatory d declination
     """
-    return numpy.arctan2(e, h)
+    return numpy.arctan2(v, u)
 
 
 def get_obs_d_from_mag_d(d, d0=0):
@@ -318,8 +318,8 @@ def get_obs_d_from_mag_d(d, d0=0):
     return numpy.subtract(d, d0)
 
 
-def get_obs_e_from_mag(h, d, d0=0):
-    """gets the observatory e component given the magnetic components.
+def get_obs_v_from_mag(h, d, d0=0):
+    """gets the observatory v component given the magnetic components.
 
     Parameters
     __________
@@ -333,32 +333,32 @@ def get_obs_e_from_mag(h, d, d0=0):
     Returns
     _______
     array_like
-        the observatory e component
+        the observatory v component
     """
     obs_d = get_obs_d_from_mag_d(d, d0)
     return numpy.multiply(h, numpy.sin(obs_d))
 
 
-def get_obs_e_from_obs(h, d):
-    """gets the observatory e component given the observatory components.
+def get_obs_v_from_obs(h, d):
+    """gets the observatory v component given the observatory components.
 
     Parameters
     __________
-    h: array_like
-        the observatory h component.
+    u: array_like
+        the observatory u component.
     d: array_like
         the observatory d declination.
 
     Returns
     _______
     array_like
-        the observatory e component
+        the observatory v component
     """
     return numpy.multiply(h, numpy.tan(d))
 
 
-def get_obs_h_from_mag(h, d, d0=0):
-    """gets the observatory h component given the magnetic north components
+def get_obs_u_from_mag(h, d, d0=0):
+    """gets the observatory u component given the magnetic north components
 
     Parameters
     __________
@@ -372,7 +372,7 @@ def get_obs_h_from_mag(h, d, d0=0):
     Returns
     _______
     array_like
-        the observatory h component
+        the observatory u component
     """
     obs_d = get_obs_d_from_mag_d(d, d0)
     return numpy.multiply(h, numpy.cos(obs_d))
@@ -405,7 +405,7 @@ def get_computed_f_using_squares(x, y, z):
     Notes
     -----
     This works for geographic coordinates, or observatory coordinates.
-        ie x, y, z or h, e, z
+        ie x, y, z or u, v, w
         We're using variables x,y,z to represent generic cartisian coordinates.
     """
     x2 = numpy.square(x)
diff --git a/geomagio/StreamConverter.py b/geomagio/StreamConverter.py
index 4ef3232c5db1ef5b0ec7cf4eb62357a9d5550413..4677acee27626dc968216793895ba4102668b302 100644
--- a/geomagio/StreamConverter.py
+++ b/geomagio/StreamConverter.py
@@ -3,7 +3,7 @@
 We use three coordinate systems.
 Geo: Based on Geographic North.  X, Y, Z, F
      X is north, Y is east, Z is down
-Obs: Based on the observatories orientaion. H, E, Z, F, d0
+Obs: Based on the observatories orientaion. U, V, W, F, d0
 Mag: Based on Magnetic North. H, D, Z, F
 """
 from __future__ import absolute_import
@@ -48,7 +48,7 @@ def get_geo_from_obs(obs):
     Parameters
     ----------
     stream : obspy.core.Stream
-        stream containing observatory components H, D or E, Z, and F.
+        stream containing observatory components U, D or V, W, and F.
 
     Returns
     -------
@@ -86,20 +86,20 @@ def get_deltaf_from_obs(obs):
     Parameters
     ----------
     obs: obspy.core.Stream
-        stream containing the observatory components H, D or E, Z, and F.
+        stream containing the observatory components U, D or V, W, and F.
 
     Returns
     -------
     obspy.core.Stream
         stream object containing delta f values
     """
-    h = obs.select(channel="H")[0]
-    z = obs.select(channel="Z")[0]
+    u = obs.select(channel="U")[0]
+    w = obs.select(channel="W")[0]
     fs = obs.select(channel="F")[0]
-    e = __get_obs_e_from_obs(obs)
-    fv = ChannelConverter.get_computed_f_using_squares(h, e, z)
+    v = __get_obs_v_from_obs(obs)
+    fv = ChannelConverter.get_computed_f_using_squares(u, v, w)
     G = ChannelConverter.get_deltaf(fv, fs)
-    return obspy.core.Stream((__get_trace("G", h.stats, G),))
+    return obspy.core.Stream((__get_trace("G", u.stats, G),))
 
 
 def get_mag_from_geo(geo):
@@ -137,28 +137,28 @@ def get_mag_from_obs(obs):
     Parameters
     ----------
     obs : obspy.core.Stream
-        stream containing observatory components H, D or E, Z, and F.
+        stream containing observatory components U, D or V, W, and F.
 
     Returns
     -------
     obspy.core.Stream
         new stream object containing magnetic components H, D, Z, and F.
     """
-    h = obs.select(channel="H")[0]
-    e = __get_obs_e_from_obs(obs)
-    z = obs.select(channel="Z")
+    u = obs.select(channel="U")[0]
+    v = __get_obs_v_from_obs(obs)
+    w = obs.select(channel="W")
     f = obs.select(channel="F")
-    obs_h = h.data
-    obs_e = e.data
+    obs_u = u.data
+    obs_v = v.data
     d0 = ChannelConverter.get_radians_from_minutes(
-        numpy.float64(e.stats.declination_base) / 10
+        numpy.float64(v.stats.declination_base) / 10
     )
-    (mag_h, mag_d) = ChannelConverter.get_mag_from_obs(obs_h, obs_e, d0)
+    (mag_h, mag_d) = ChannelConverter.get_mag_from_obs(obs_u, obs_v, d0)
     return (
         obspy.core.Stream(
-            (__get_trace("H", h.stats, mag_h), __get_trace("D", e.stats, mag_d))
+            (__get_trace("H", u.stats, mag_h), __get_trace("D", v.stats, mag_d))
         )
-        + z
+        + w
         + f
     )
 
@@ -193,7 +193,7 @@ def get_obs_from_mag(mag, include_d=False):
     Returns
     -------
     obspy.core.Stream
-        new stream object containing observatory components H, D, E, Z, and F
+        new stream object containing observatory components U, D, V, W, and F
     """
     h = mag.select(channel="H")[0]
     d = mag.select(channel="D")[0]
@@ -205,43 +205,43 @@ def get_obs_from_mag(mag, include_d=False):
     d0 = ChannelConverter.get_radians_from_minutes(
         numpy.float64(d.stats.declination_base) / 10
     )
-    (obs_h, obs_e) = ChannelConverter.get_obs_from_mag(mag_h, mag_d, d0)
+    (obs_u, obs_v) = ChannelConverter.get_obs_from_mag(mag_h, mag_d, d0)
 
-    traces = (__get_trace("H", h.stats, obs_h), __get_trace("E", d.stats, obs_e))
+    traces = (__get_trace("U", h.stats, obs_u), __get_trace("V", d.stats, obs_v))
     if include_d:
-        obs_d = ChannelConverter.get_obs_d_from_obs(obs_h, obs_e)
+        obs_d = ChannelConverter.get_obs_d_from_obs(obs_u, obs_v)
         traces = traces + (__get_trace("D", d.stats, obs_d),)
     return obspy.core.Stream(traces) + z + f
 
 
-def get_obs_from_obs(obs, include_e=False, include_d=False):
+def get_obs_from_obs(obs, include_v=False, include_d=False):
     """Fill in the observatory parameters as requested
 
     Parameters
     ----------
     stream: obspy.core.Stream
-        stream containing the observatory components H, D or E, Z, and F.
+        stream containing the observatory components U, D or V, W, and F.
     include_e: boolean
-        whether to include the e component
+        whether to include the v component
     include_d: boolean
         whether to include the d component
 
     Returns
     -------
     obspy.core.Stream
-        new stream object containing observatory components H, D, E, Z, and F
+        new stream object containing observatory components U, D, V, W, and F
     """
-    h = obs.select(channel="H")[0]
-    z = obs.select(channel="Z")
+    u = obs.select(channel="U")[0]
+    w = obs.select(channel="W")
     f = obs.select(channel="F")
-    traces = (h,)
+    traces = (u,)
     if include_d:
         d = __get_obs_d_from_obs(obs)
         traces = traces + (d,)
-    if include_e:
-        e = __get_obs_e_from_obs(obs)
-        traces = traces + (e,)
-    return obspy.core.Stream(traces) + z + f
+    if include_v:
+        v = __get_obs_v_from_obs(obs)
+        traces = traces + (v,)
+    return obspy.core.Stream(traces) + w + f
 
 
 def __get_trace(channel, stats, data):
@@ -269,12 +269,12 @@ def __get_trace(channel, stats, data):
 def __get_obs_d_from_obs(obs):
     """Get trace containing observatory D component.
 
-    Returns D if found, otherwise computes D from H, E, D0.
+    Returns D if found, otherwise computes D from U, V, D0.
 
     Parameters
     ----------
     obs : obspy.core.Stream
-        observatory components (D) or (H, E).
+        observatory components (D) or (U, V).
 
     Returns
     -------
@@ -284,35 +284,35 @@ def __get_obs_d_from_obs(obs):
     try:
         d = obs.select(channel="D")[0]
     except IndexError:
-        h = obs.select(channel="H")[0]
-        e = obs.select(channel="E")[0]
+        u = obs.select(channel="U")[0]
+        v = obs.select(channel="V")[0]
         d = __get_trace(
-            "D", e.stats, ChannelConverter.get_obs_d_from_obs(h.data, e.data)
+            "D", v.stats, ChannelConverter.get_obs_d_from_obs(u.data, v.data)
         )
     return d
 
 
-def __get_obs_e_from_obs(obs):
-    """Get trace containing observatory E component.
+def __get_obs_v_from_obs(obs):
+    """Get trace containing observatory V component.
 
-    Returns E if found, otherwise computes E from H,D.
+    Returns V if found, otherwise computes V from U,D.
 
     Parameters
     ----------
     obs : obspy.core.Stream
-        observatory components (E) or (H, D).
+        observatory components (V) or (U, D).
 
     Returns
     -------
     obspy.core.Trace
-        observatory component E.
+        observatory component V.
     """
     try:
-        e = obs.select(channel="E")[0]
+        v = obs.select(channel="V")[0]
     except IndexError:
-        h = obs.select(channel="H")[0]
+        u = obs.select(channel="U")[0]
         d = obs.select(channel="D")[0]
-        e = __get_trace(
-            "E", d.stats, ChannelConverter.get_obs_e_from_obs(h.data, d.data)
+        v = __get_trace(
+            "V", d.stats, ChannelConverter.get_obs_v_from_obs(u.data, d.data)
         )
-    return e
+    return v
diff --git a/geomagio/TimeseriesUtility.py b/geomagio/TimeseriesUtility.py
index e0729c21b81796707a06cff1184765aeab5be29c..f5aa9856064eeb71b3d6bd19eeea24b4925b819e 100644
--- a/geomagio/TimeseriesUtility.py
+++ b/geomagio/TimeseriesUtility.py
@@ -610,6 +610,8 @@ def split_trace(trace: Trace, size: int = 86400) -> Stream:
         size=size,
         trim=True,
     ):
+        if interval["start"] == interval["end"]:
+            return Stream([out_trace])
         stream += out_trace.slice(
             starttime=interval["start"],
             endtime=interval["end"] - out_trace.stats.delta,
diff --git a/geomagio/adjusted/AdjustedMatrix.py b/geomagio/adjusted/AdjustedMatrix.py
index 88e7169bff4200f65eb1899cfa89077afbafab5b..f02671ae2161d2e751b758b13cead236f97e73e1 100644
--- a/geomagio/adjusted/AdjustedMatrix.py
+++ b/geomagio/adjusted/AdjustedMatrix.py
@@ -32,7 +32,7 @@ class AdjustedMatrix(BaseModel):
     def process(
         self,
         stream: Stream,
-        inchannels=["H", "E", "Z", "F"],
+        inchannels=["U", "V", "W", "F"],
         outchannels=["X", "Y", "Z", "F"],
     ):
         """ Apply matrix to raw data. Apply pier correction to F when necessary """
diff --git a/geomagio/algorithm/AdjustedAlgorithm.py b/geomagio/algorithm/AdjustedAlgorithm.py
index fcbe2dcebb8e66e2d95e9235a7134e905f52eca6..ef92e2ed471e8b7e767f28b0a23a6377961b2065 100644
--- a/geomagio/algorithm/AdjustedAlgorithm.py
+++ b/geomagio/algorithm/AdjustedAlgorithm.py
@@ -23,7 +23,7 @@ class AdjustedAlgorithm(Algorithm):
         inchannels=None,
         outchannels=None,
     ):
-        inchannels = inchannels or ["H", "E", "Z", "F"]
+        inchannels = inchannels or ["U", "V", "W", "F"]
         outchannels = outchannels or ["X", "Y", "Z", "F"]
         Algorithm.__init__(
             self,
diff --git a/geomagio/algorithm/DeltaFAlgorithm.py b/geomagio/algorithm/DeltaFAlgorithm.py
index 22ebab00988a260a6fc986048ef076caa5b2ad2a..bea31af9d77271fe16d39a00fcd63a99327ab5e0 100644
--- a/geomagio/algorithm/DeltaFAlgorithm.py
+++ b/geomagio/algorithm/DeltaFAlgorithm.py
@@ -10,10 +10,10 @@ from .. import StreamConverter
 # List of channels by geomagnetic observatory orientation.
 # geo represents a geographic north/south orientation
 # obs represents the sensor orientation aligned close to the mag orientation
-# obsd is the same as obs,  but with D(declination) instead of E (e/w vector)
+# obsd is the same as obs,  but with D(declination) instead of V (e/w vector)
 CHANNELS = {
     "geo": ["X", "Y", "Z", "F"],
-    "obs": ["H", "E", "Z", "F"],
+    "obs": ["U", "V", "W", "F"],
     "obsd": ["H", "D", "Z", "F"],
 }
 
diff --git a/geomagio/algorithm/FilterAlgorithm.py b/geomagio/algorithm/FilterAlgorithm.py
index da0f2df18f115f7be5d9e3d25c86147917a65e4a..861d127c944e6401fe10cb4077a5de8e6c88b125 100644
--- a/geomagio/algorithm/FilterAlgorithm.py
+++ b/geomagio/algorithm/FilterAlgorithm.py
@@ -411,8 +411,8 @@ class FilterAlgorithm(Algorithm):
         steps = self.get_filter_steps()
         # calculate start/end from inverted step array
         for step in reversed(steps):
-            start_interval = get_nearest_time(step=step, output_time=start, left=False)
-            end_interval = get_nearest_time(step=step, output_time=end, left=True)
+            start_interval = get_nearest_time(step=step, output_time=start, left=True)
+            end_interval = get_nearest_time(step=step, output_time=end, left=False)
             start, end = start_interval["data_start"], end_interval["data_end"]
         return (start, end)
 
diff --git a/geomagio/algorithm/XYZAlgorithm.py b/geomagio/algorithm/XYZAlgorithm.py
index 1f43c87853c8da21d7a4636ad1bf7560f35e5471..7c66bc07cd67844e03c739f50b620f6b609b60dd 100644
--- a/geomagio/algorithm/XYZAlgorithm.py
+++ b/geomagio/algorithm/XYZAlgorithm.py
@@ -12,11 +12,11 @@ from .. import StreamConverter
 # geo represents a geographic north/south orientation
 # mag represents the (calculated)instantaneous mangnetic north orientation
 # obs represents the sensor orientation aligned close to the mag orientation
-# obsd is the same as obs,  but with D(declination) instead of E (e/w vector)
+# obsd is the same as obs,  but with D(declination) instead of V (e/w vector)
 CHANNELS = {
     "geo": ["X", "Y", "Z", "F"],
     "mag": ["H", "D", "Z", "F"],
-    "obs": ["H", "E", "Z", "F"],
+    "obs": ["U", "V", "W", "F"],
     "obsd": ["H", "D", "Z", "F"],
 }
 
diff --git a/geomagio/edge/EdgeFactory.py b/geomagio/edge/EdgeFactory.py
index e0f1091c59cab35a47b07ed2b5150641cf847440..686466f88ad62ff0f0ffa46ed86981e7cf9306c7 100644
--- a/geomagio/edge/EdgeFactory.py
+++ b/geomagio/edge/EdgeFactory.py
@@ -199,6 +199,11 @@ class EdgeFactory(TimeseriesFactory):
             channel and that trace should have an ndarray, with nan's
             representing gaps.
         """
+        if not TimeseriesUtility.has_any_channels(
+            stream=timeseries, channels=channels, starttime=starttime, endtime=endtime
+        ):
+            sys.stderr.write("No data to write\n")
+            return
         stats = timeseries[0].stats
         observatory = observatory or stats.station or self.observatory
         channels = channels or self.channels
@@ -308,13 +313,13 @@ class EdgeFactory(TimeseriesFactory):
 
         if channel == "D":
             edge_channel = edge_interval_code + "VD"
-        elif channel == "E":
+        elif channel in ["E", "V"]:
             edge_channel = edge_interval_code + "VE"
         elif channel == "F":
             edge_channel = edge_interval_code + "SF"
-        elif channel == "H":
+        elif channel in ["H", "U"]:
             edge_channel = edge_interval_code + "VH"
-        elif channel == "Z":
+        elif channel in ["Z", "W"]:
             edge_channel = edge_interval_code + "VZ"
         elif channel == "G":
             edge_channel = edge_interval_code + "SG"
diff --git a/geomagio/edge/MiniSeedFactory.py b/geomagio/edge/MiniSeedFactory.py
index 99c3b9eec31e63a125131b6c0f65933220b13822..eb1397f2e15f4845fe945d2d129f574a1249c80b 100644
--- a/geomagio/edge/MiniSeedFactory.py
+++ b/geomagio/edge/MiniSeedFactory.py
@@ -67,7 +67,7 @@ class MiniSeedFactory(TimeseriesFactory):
         self,
         host="cwbpub.cr.usgs.gov",
         port=2061,
-        write_port=7981,
+        write_port=7905,
         observatory=None,
         channels=None,
         type=None,
@@ -190,6 +190,11 @@ class MiniSeedFactory(TimeseriesFactory):
             channel and that trace should have an ndarray, with nan's
             representing gaps.
         """
+        if not TimeseriesUtility.has_any_channels(
+            stream=timeseries, channels=channels, starttime=starttime, endtime=endtime
+        ):
+            sys.stderr.write("No data to write\n")
+            return
         stats = timeseries[0].stats
         observatory = observatory or stats.station or self.observatory
         channels = channels or self.channels
diff --git a/geomagio/edge/MiniSeedInputClient.py b/geomagio/edge/MiniSeedInputClient.py
index a25d31ce89741d8483aa9a1bf55220b1d2c3358e..d2dc2e148e6bb8351b846bc2ccca4d268a544a35 100644
--- a/geomagio/edge/MiniSeedInputClient.py
+++ b/geomagio/edge/MiniSeedInputClient.py
@@ -25,7 +25,7 @@ class MiniSeedInputClient(object):
         Floating point precision for output data
     """
 
-    def __init__(self, host, port=2061, encoding="float32"):
+    def __init__(self, host, port=2061, encoding="float64"):
         self.host = host
         self.port = port
         self.encoding = encoding
diff --git a/geomagio/processing/__init__.py b/geomagio/processing/__init__.py
index be97cfbd5e32485df54fc814fd646c595685fed7..b395a8b0beccb9c28df42b2be3b30d61c8083013 100644
--- a/geomagio/processing/__init__.py
+++ b/geomagio/processing/__init__.py
@@ -4,8 +4,14 @@ Note that these implementations are subject to change,
 and should be considered less stable than other packages in the library.
 """
 from .factory import get_edge_factory, get_miniseed_factory
-from .observatory import adjusted, average, deltaf, rotate, sqdist_minute
-from .obsrio import obsrio_minute, obsrio_second, obsrio_temperatures, obsrio_tenhertz
+from .derived import adjusted, average, deltaf, rotate, sqdist_minute
+from .obsrio import (
+    filter_day,
+    filter_hour,
+    filter_minute,
+    filter_temperatures,
+    filter_tenhertz,
+)
 
 
 __all__ = [
@@ -14,10 +20,11 @@ __all__ = [
     "deltaf",
     "get_edge_factory",
     "get_miniseed_factory",
-    "obsrio_minute",
-    "obsrio_second",
-    "obsrio_temperatures",
-    "obsrio_tenhertz",
+    "filter_day",
+    "filter_hour",
+    "filter_minute",
+    "filter_temperatures",
+    "filter_tenhertz",
     "rotate",
     "sqdist_minute",
 ]
diff --git a/geomagio/processing/derived.py b/geomagio/processing/derived.py
new file mode 100644
index 0000000000000000000000000000000000000000..3fbf24b77e4091dc864c7421ab1cad9cfeee06fb
--- /dev/null
+++ b/geomagio/processing/derived.py
@@ -0,0 +1,319 @@
+from typing import List, Literal, Optional
+
+from obspy import UTCDateTime
+
+from ..adjusted import AdjustedMatrix
+from ..algorithm import (
+    AdjustedAlgorithm,
+    AverageAlgorithm,
+    DeltaFAlgorithm,
+    SqDistAlgorithm,
+    XYZAlgorithm,
+)
+from ..Controller import Controller, get_realtime_interval
+from ..metadata import MetadataCategory, MetadataFactory, MetadataQuery
+from ..TimeseriesFactory import TimeseriesFactory
+from .factory import get_miniseed_factory
+
+
+def adjusted(
+    observatory: str,
+    input_factory: Optional[TimeseriesFactory] = None,
+    output_factory: Optional[TimeseriesFactory] = None,
+    realtime_interval: int = 600,
+    update_limit: int = 10,
+):
+    """Run Adjusted algorithm.
+
+    Parameters
+    ----------
+    observatory: observatory to calculate
+    input_factory: where to read, should be configured with data_type
+    output_factory: where to write, should be configured with data_type
+    realtime_interval: window in seconds
+
+    Uses update_limit=10.
+    """
+    starttime, endtime = get_realtime_interval(realtime_interval)
+    controller = Controller(
+        algorithm=AdjustedAlgorithm(
+            matrix=get_adjusted_matrix(observatory=observatory, time=endtime),
+            data_type="adjusted",
+            location="A0",
+        ),
+        inputFactory=input_factory or get_miniseed_factory(data_type="variation"),
+        inputInterval="second",
+        outputFactory=output_factory or get_miniseed_factory(data_type="adjusted"),
+        outputInterval="second",
+    )
+    controller.run_as_update(
+        observatory=(observatory,),
+        output_observatory=(observatory,),
+        starttime=starttime,
+        endtime=endtime,
+        input_channels=("U", "V", "W", "F"),
+        output_channels=("X", "Y", "Z", "F"),
+        realtime=realtime_interval,
+        update_limit=update_limit,
+    )
+
+
+def average(
+    observatories: List[str],
+    input_channel: str,
+    input_factory: Optional[TimeseriesFactory] = None,
+    output_channel: Optional[str] = None,
+    output_factory: Optional[TimeseriesFactory] = None,
+    output_observatory: str = "USGS",
+    realtime_interval: int = 600,
+):
+    """Run Average algorithm.
+
+    Parameters
+    ----------
+    observatories: input observatories to calculate
+    input_channel: channel from multiple observatories to average
+    input_factory: where to read, should be configured with data_type and interval
+    interval: data interval
+    output_channel: channel to write (defaults to input_channel).
+    output_factory: where to write, should be configured with data_type and interval
+    output_observatory: observatory where output is written
+    realtime_interval: window in seconds
+
+    Uses update_limit=10.
+    """
+    starttime, endtime = get_realtime_interval(realtime_interval)
+    controller = Controller(
+        algorithm=AverageAlgorithm(observatories=observatories, channel=output_channel),
+        inputFactory=input_factory or get_miniseed_factory(data_type="variation"),
+        inputInterval="minute",
+        outputFactory=output_factory or get_miniseed_factory(data_type="variation"),
+        outputInterval="minute",
+    )
+    controller.run_as_update(
+        observatory=observatories,
+        output_observatory=(output_observatory,),
+        starttime=starttime,
+        endtime=endtime,
+        output_channels=(output_channel or input_channel,),
+        realtime=realtime_interval,
+        update_limit=10,
+    )
+
+
+def deltaf(
+    observatory: str,
+    data_type: Literal["adjusted", "variation"],
+    interval: str,
+    input_factory: Optional[TimeseriesFactory] = None,
+    output_factory: Optional[TimeseriesFactory] = None,
+    deltaf_from="obs",
+    realtime_interval: int = 600,
+    update_limit: int = 10,
+):
+    """Run Delta-F algorithm.
+
+    Parameters
+    ----------
+    observatory: observatory to calculate
+    data_type: factory data type
+    interval: data interval
+    input_factory: where to read, should be configured with data_type and interval
+    output_factory: where to write, should be configured with data_type and interval
+    deltaf_from: one of {"obs", "mag", "geo"}
+    realtime_interval: window in seconds
+
+    Uses update_limit=10.
+    """
+    starttime, endtime = get_realtime_interval(realtime_interval)
+    controller = Controller(
+        algorithm=DeltaFAlgorithm(informat=deltaf_from),
+        inputFactory=input_factory or get_miniseed_factory(data_type=data_type),
+        inputInterval=interval,
+        outputFactory=output_factory or get_miniseed_factory(data_type=data_type),
+        outputInterval=interval,
+    )
+    controller.run_as_update(
+        observatory=(observatory,),
+        output_observatory=(observatory,),
+        starttime=starttime,
+        endtime=endtime,
+        output_channels=("G",),
+        realtime=realtime_interval,
+        update_limit=update_limit,
+    )
+
+
+def get_adjusted_matrix(observatory: str, time: UTCDateTime) -> AdjustedMatrix:
+    """gets most recent valid adjusted matrix(within past month)"""
+    query = MetadataQuery(
+        station=observatory,
+        category=MetadataCategory.ADJUSTED_MATRIX,
+        created_time=time - (86400 * 31),
+    )
+    metas = MetadataFactory().get_metadata(query=query)
+    try:
+        return AdjustedMatrix(**metas[-1].metadata)
+    except:
+        raise ValueError("No valid adjusted matrix found")
+
+
+def process_adjusted(
+    observatory: str,
+    interval: str,
+    realtime_interval: int = 600,
+    update_limit: int = 10,
+):
+    """compute 1 second/minute adjusted variation channels"""
+    rotate(
+        observatory=observatory,
+        data_type="adjusted",
+        interval=interval,
+        output_channels=("H", "D"),
+        xyz_from="geo",
+        xyz_to="mag",
+        realtime_interval=realtime_interval,
+        update_limit=update_limit,
+    )
+    deltaf(
+        observatory=observatory,
+        data_type="adjusted",
+        deltaf_from="geo",
+        interval=interval,
+        realtime_interval=realtime_interval,
+        update_limit=update_limit,
+    )
+
+
+def process_variation(
+    observatory: str,
+    interval: str,
+    realtime_interval: int = 600,
+    update_limit: int = 10,
+):
+    """compute 1 second/minute derived variation channels"""
+    rotate(
+        observatory=observatory,
+        data_type="variation",
+        interval=interval,
+        xyz_from="obs",
+        xyz_to="geo",
+        realtime_interval=realtime_interval,
+        update_limit=update_limit,
+    )
+    rotate(
+        observatory=observatory,
+        data_type="variation",
+        interval=interval,
+        output_channels=("D",),
+        xyz_from="obs",
+        xyz_to="obsd",
+        realtime_interval=realtime_interval,
+        update_limit=update_limit,
+    )
+    deltaf(
+        observatory=observatory,
+        interval=interval,
+        data_type="variation",
+        realtime_interval=realtime_interval,
+        update_limit=update_limit,
+    )
+
+
+def rotate(
+    observatory: str,
+    data_type: Literal["adjusted", "variation"],
+    interval: str,
+    input_factory: Optional[TimeseriesFactory] = None,
+    output_channels=("X", "Y"),
+    output_factory: Optional[TimeseriesFactory] = None,
+    xyz_from="obs",
+    xyz_to="geo",
+    realtime_interval: int = 600,
+    update_limit: int = 10,
+):
+    """Run XYZ rotation algorithm.
+
+    Parameters
+    ----------
+    observatory: observatory to calculate
+    data_type: factory data type
+    interval: data interval
+    input_factory: where to read, should be configured with data_type and interval
+    output_channels: which channels to write
+    output_factory: where to write, should be configured with data_type and interval
+    realtime_interval: window in seconds
+    xyz_from: one of {"obs", "mag", "geo"}
+    xyz_to: one of {"obs", "obsd", "mag", "geo"}
+
+    Uses update_limit=10.
+    """
+    starttime, endtime = get_realtime_interval(realtime_interval)
+    controller = Controller(
+        algorithm=XYZAlgorithm(informat=xyz_from, outformat=xyz_to),
+        inputFactory=input_factory or get_miniseed_factory(data_type=data_type),
+        inputInterval=interval,
+        outputFactory=output_factory or get_miniseed_factory(data_type=data_type),
+        outputInterval=interval,
+    )
+    controller.run_as_update(
+        observatory=(observatory,),
+        output_observatory=(observatory,),
+        starttime=starttime,
+        endtime=endtime,
+        output_channels=output_channels,
+        realtime=realtime_interval,
+        update_limit=update_limit,
+    )
+
+
+def sqdist_minute(
+    observatory: str,
+    data_type: Literal["adjusted", "variation"],
+    statefile: str,
+    input_factory: Optional[TimeseriesFactory] = None,
+    output_factory: Optional[TimeseriesFactory] = None,
+    realtime_interval: int = 1800,
+):
+    """Run SqDist algorithm.
+
+    Only supports "minute" interval.
+
+    Parameters
+    ----------
+    observatory: observatory to calculate
+    data_type: factory data type
+    statefile: sqdist statefile must already exist
+    input_factory: where to read, should be configured with data_type and interval
+    output_factory: where to write, should be configured with data_type and interval
+    realtime_interval: window in seconds
+    """
+    if not statefile:
+        raise ValueError("Statefile is required.")
+    if realtime_interval < 1800:
+        realtime_interval = 1800
+    starttime, endtime = get_realtime_interval(realtime_interval)
+    controller = Controller(
+        algorithm=SqDistAlgorithm(
+            alpha=2.3148e-5,
+            gamma=3.3333e-2,
+            m=1440,
+            mag=True,
+            smooth=180,
+            beta=0,
+            statefile=statefile,
+        ),
+        inputFactory=input_factory or get_miniseed_factory(data_type=data_type),
+        inputInterval="minute",
+        outputFactory=output_factory or get_miniseed_factory(data_type=data_type),
+        outputInterval="minute",
+    )
+    # sqdist is stateful, use run
+    controller.run(
+        observatory=(observatory,),
+        starttime=starttime,
+        endtime=endtime,
+        input_channels=("X", "Y", "Z", "F"),
+        output_channels=("H_SQ", "H_SV", "H_Dist"),
+        realtime=realtime_interval,
+    )
diff --git a/geomagio/processing/factory.py b/geomagio/processing/factory.py
index d9a065497d9cf6b0a38c391355e11a9d698795d2..1838cd74c756dd9b9fae54fe0bffd99c2c17077c 100644
--- a/geomagio/processing/factory.py
+++ b/geomagio/processing/factory.py
@@ -19,7 +19,6 @@ def get_miniseed_factory(
     data_type="variation", interval="second", **kwargs
 ) -> TimeseriesFactory:
     return MiniSeedFactory(
-        convert_channels=("U", "V", "W"),
         host=os.getenv("EDGE_HOST", "127.0.0.1"),
         interval=interval,
         type=data_type,
diff --git a/geomagio/processing/observatory.py b/geomagio/processing/observatory.py
index 85a9cfd7f033c4e72bb3401456f939c4e154a8c1..fd579277f840f6d5d1862f5e7536bcb958e5b36b 100644
--- a/geomagio/processing/observatory.py
+++ b/geomagio/processing/observatory.py
@@ -1,243 +1,225 @@
-from typing import List, Optional
+from enum import Enum
 
-import numpy
+import typer
 
-from ..algorithm import (
-    AdjustedAlgorithm,
-    AverageAlgorithm,
-    DeltaFAlgorithm,
-    SqDistAlgorithm,
-    XYZAlgorithm,
+from . import derived
+from . import obsrio
+from . import pcdcp
+
+app = typer.Typer(
+    help=f"""
+    Command line interface for processing pipeline
+    """
 )
-from ..Controller import Controller, get_realtime_interval
-from ..TimeseriesFactory import TimeseriesFactory
-from .factory import get_edge_factory
 
 
-def adjusted(
+class DataFormat(str, Enum):
+    OBSRIO = "obsrio"
+    PCDCP = "pcdcp"
+
+
+class DataType(str, Enum):
+    ADJUSTED = "adjusted"
+    VARIATION = "variation"
+
+
+def main():
+    app()
+
+
+@app.command(
+    name="adjusted",
+    help=f"""
+    transforms, filters, and derives data at 1Hz and 1 min.
+
+    Applies adjusted algorithm at 1Hz: U,V,W,F -> X,Y,Z,F
+
+    Dervies 1Hz adjusted channels: X,Y,H,D,G
+
+    Filters 1Hz adjusted to 1 min adjusted
+
+    Dervies 1 min adjusted channels: X,Y,H,D,G
+
+    Applies SQDistAlgorithm to 1 min adjused: H_SQ, H_SV, H_Dist
+    """,
+)
+def process_adjusted(
     observatory: str,
-    input_factory: Optional[TimeseriesFactory] = None,
-    interval: str = "second",
-    output_factory: Optional[TimeseriesFactory] = None,
-    matrix: Optional[numpy.ndarray] = None,
-    pier_correction: Optional[float] = None,
-    statefile: Optional[str] = None,
+    sqdist_statefile: str,
     realtime_interval: int = 600,
+    update_limit: int = 10,
 ):
-    """Run Adjusted algorithm.
-
-    Parameters
-    ----------
-    observatory: observatory to calculate
-    input_factory: where to read, should be configured with data_type
-    interval: data interval
-    output_factory: where to write, should be configured with data_type
-    matrix: adjusted matrix
-    pier_correction: adjusted pier correction
-    statefile: adjusted statefile
-    realtime_interval: window in seconds
-
-    Uses update_limit=10.
-    """
-    if not statefile and (not matrix or not pier_correction):
-        raise ValueError("Either statefile or matrix and pier_correction are required.")
-    starttime, endtime = get_realtime_interval(realtime_interval)
-    controller = Controller(
-        algorithm=AdjustedAlgorithm(
-            matrix=matrix,
-            pier_correction=pier_correction,
-            statefile=statefile,
-            data_type="adjusted",
-            location="A0",
-        ),
-        inputFactory=input_factory or get_edge_factory(data_type="variation"),
-        inputInterval=interval,
-        outputFactory=output_factory or get_edge_factory(data_type="adjusted"),
-        outputInterval=interval,
+    derived.adjusted(
+        observatory=observatory,
+        realtime_interval=realtime_interval,
+        update_limit=update_limit,
+    )
+    derived.process_adjusted(
+        observatory=observatory,
+        interval="second",
+        realtime_interval=realtime_interval,
+        update_limit=update_limit,
+    )
+    obsrio.filter_minute(
+        observatory=observatory,
+        data_type="adjusted",
+        channels=["X", "Y", "Z", "F"],
+        realtime_interval=realtime_interval,
+        update_limit=update_limit,
+    )
+    derived.process_adjusted(
+        observatory=observatory,
+        interval="minute",
+        realtime_interval=realtime_interval,
+        update_limit=update_limit,
+    )
+    derived.sqdist_minute(
+        observatory=observatory,
+        data_type="adjusted",
+        statefile=sqdist_statefile,
+        realtime_interval=realtime_interval,
     )
-    controller.run_as_update(
-        observatory=(observatory,),
-        output_observatory=(observatory,),
-        starttime=starttime,
-        endtime=endtime,
-        input_channels=("H", "E", "Z", "F"),
-        output_channels=("X", "Y", "Z", "F"),
-        realtime=realtime_interval,
-        update_limit=10,
+    pcdcp.legacy(
+        observatory=observatory,
+        data_type="adjusted",
+        realtime_interval=realtime_interval,
+        update_limit=update_limit,
     )
 
 
-def average(
-    observatories: List[str],
-    input_channel: str,
-    input_factory: Optional[TimeseriesFactory] = None,
-    interval: str = "second",
-    output_channel: str = None,
-    output_factory: Optional[TimeseriesFactory] = None,
-    output_observatory: str = "USGS",
-    realtime_interval: int = 600,
+@app.command(
+    name="obsrio",
+    help=f"""
+    filters and derives variation/adjusted data at 1 hour/day.
+
+    Filters 1 min to 1 hour/day
+
+    Dervies 1 hour/day derived channels: X,Y,H(adjusted),D,G
+    """,
+)
+def process_obsrio(
+    observatory: str,
+    data_type: str,
+    realtime_interval: int = 86400,
+    update_limit: int = 7,
 ):
-    """Run Average algorithm.
-
-    Parameters
-    ----------
-    observatories: input observatories to calculate
-    input_channel: channel from multiple observatories to average
-    input_factory: where to read, should be configured with data_type and interval
-    interval: data interval
-    output_channel: channel to write (defaults to input_channel).
-    output_factory: where to write, should be configured with data_type and interval
-    output_observatory: observatory where output is written
-    realtime_interval: window in seconds
-
-    Uses update_limit=10.
-    """
-    starttime, endtime = get_realtime_interval(realtime_interval)
-    controller = Controller(
-        algorithm=AverageAlgorithm(observatories=observatories, channel=output_channel),
-        inputFactory=input_factory or get_edge_factory(),
-        inputInterval=interval,
-        outputFactory=output_factory or get_edge_factory(),
-        outputInterval=interval,
+    channels = (
+        ("X", "Y", "Z", "F") if data_type == "adjusted" else ("U", "V", "W", "Z", "F")
     )
-    controller.run_as_update(
-        observatory=observatories,
-        output_observatory=(output_observatory,),
-        starttime=starttime,
-        endtime=endtime,
-        output_channels=(output_channel or input_channel,),
-        realtime=realtime_interval,
-        update_limit=10,
+    obsrio.filter_day(
+        observatory=observatory,
+        data_type=data_type,
+        channels=channels,
     )
+    obsrio.filter_hour(
+        observatory=observatory,
+        data_type=data_type,
+        channels=channels,
+    )
+    if data_type == DataType.ADJUSTED:
+        derived.process_adjusted(
+            observatory=observatory,
+            interval="hour",
+            realtime_interval=realtime_interval,
+            update_limit=update_limit,
+        )
+        derived.process_adjusted(
+            observatory=observatory,
+            interval="day",
+            realtime_interval=realtime_interval,
+            update_limit=update_limit,
+        )
+    elif data_type == DataType.VARIATION:
+        derived.process_variation(
+            observatory=observatory,
+            interval="hour",
+            realtime_interval=realtime_interval,
+            update_limit=update_limit,
+        )
+        derived.process_variation(
+            observatory=observatory,
+            interval="day",
+            realtime_interval=realtime_interval,
+            update_limit=update_limit,
+        )
 
 
-def deltaf(
-    observatory: str,
-    input_factory: Optional[TimeseriesFactory] = None,
-    interval: str = "second",
-    output_factory: Optional[TimeseriesFactory] = None,
-    deltaf_from="obs",
-    realtime_interval: int = 600,
-):
-    """Run Delta-F algorithm.
+@app.command(
+    name="variation",
+    help=f"""
+    filters and derives variation channels at 1Hz and 1min.
 
-    Parameters
-    ----------
-    observatory: observatory to calculate
-    input_factory: where to read, should be configured with data_type and interval
-    output_factory: where to write, should be configured with data_type and interval
-    deltaf_from: one of {"obs", "mag", "geo"}
-    realtime_interval: window in seconds
+    Gathers 1Hz variation data from obrio/pcdcp:
+
+        pcdcp:
+
+            copy edge channels to miniseed: H,E,Z,F,UK1-4 -> U,V,W,F,UK1-4
+
+        obsrio:
+
+            filter 10Hz miniseed to 1Hz miniseed
+
+            filter 1Hz temperatures to 1 min temperatures: LK1-4 -> UK1-4
+
+            copy 1 min temperatures from miniseed to edge
 
-    Uses update_limit=10.
-    """
-    starttime, endtime = get_realtime_interval(realtime_interval)
-    controller = Controller(
-        algorithm=DeltaFAlgorithm(informat=deltaf_from),
-        inputFactory=input_factory or get_edge_factory(),
-        inputInterval=interval,
-        outputFactory=output_factory or get_edge_factory(),
-        outputInterval=interval,
-    )
-    controller.run_as_update(
-        observatory=(observatory,),
-        output_observatory=(observatory,),
-        starttime=starttime,
-        endtime=endtime,
-        output_channels=("G",),
-        realtime=realtime_interval,
-        update_limit=10,
-    )
 
+    Dervies 1Hz variation channels: X,Y,D,G
 
-def rotate(
+    Filters 1Hz variation to 1 min variation
+
+    Dervies 1 min variation channels: X,Y,D,G
+
+    Applies SQDistAlgorithm to 1 min variation: H_SQ, H_SV, H_Dist
+    """,
+)
+def process_variation(
     observatory: str,
-    input_factory: Optional[TimeseriesFactory] = None,
-    interval: str = "second",
-    output_channels=("X", "Y"),
-    output_factory: Optional[TimeseriesFactory] = None,
+    data_format: DataFormat,
+    sqdist_statefile: str,
     realtime_interval: int = 600,
-    xyz_from="obs",
-    xyz_to="geo",
+    update_limit: int = 10,
 ):
-    """Run XYZ rotation algorithm.
-
-    Parameters
-    ----------
-    observatory: observatory to calculate
-    input_factory: where to read, should be configured with data_type and interval
-    output_channels: which channels to write
-    output_factory: where to write, should be configured with data_type and interval
-    realtime_interval: window in seconds
-    xyz_from: one of {"obs", "mag", "geo"}
-    xyz_to: one of {"obs", "obsd", "mag", "geo"}
-
-    Uses update_limit=10.
-    """
-    starttime, endtime = get_realtime_interval(realtime_interval)
-    controller = Controller(
-        algorithm=XYZAlgorithm(informat=xyz_from, outformat=xyz_to),
-        inputFactory=input_factory or get_edge_factory(),
-        inputInterval=interval,
-        outputFactory=output_factory or get_edge_factory(),
-        outputInterval=interval,
+    if data_format == DataFormat.OBSRIO:
+        obsrio.prepare(
+            observatory=observatory,
+            realtime_interval=realtime_interval,
+            update_limit=update_limit,
+        )
+    elif data_format == DataFormat.PCDCP:
+        pcdcp.prepare(
+            observatory=observatory,
+            realtime_interval=realtime_interval,
+            update_limit=update_limit,
+        )
+    derived.process_variation(
+        observatory=observatory,
+        interval="second",
+        realtime_interval=realtime_interval,
+        update_limit=update_limit,
     )
-    controller.run_as_update(
-        observatory=(observatory,),
-        output_observatory=(observatory,),
-        starttime=starttime,
-        endtime=endtime,
-        output_channels=output_channels,
-        realtime=realtime_interval,
-        update_limit=10,
+    obsrio.filter_minute(
+        observatory=observatory,
+        data_type="variation",
+        channels=["U", "V", "W", "Z", "F"],
+        realtime_interval=realtime_interval,
+        update_limit=update_limit,
     )
-
-
-def sqdist_minute(
-    observatory: str,
-    statefile: str,
-    input_factory: Optional[TimeseriesFactory] = None,
-    output_factory: Optional[TimeseriesFactory] = None,
-    realtime_interval: int = 1800,
-):
-    """Run SqDist algorithm.
-
-    Only supports "minute" interval.
-
-    Parameters
-    ----------
-    observatory: observatory to calculate
-    statefile: sqdist statefile must already exist
-    input_factory: where to read, should be configured with data_type and interval
-    output_factory: where to write, should be configured with data_type and interval
-    realtime_interval: window in seconds
-    """
-    if not statefile:
-        raise ValueError("Statefile is required.")
-    starttime, endtime = get_realtime_interval(realtime_interval)
-    controller = Controller(
-        algorithm=SqDistAlgorithm(
-            alpha=2.3148e-5,
-            gamma=3.3333e-2,
-            m=1440,
-            mag=True,
-            smooth=180,
-            statefile=statefile,
-        ),
-        inputFactory=input_factory or get_edge_factory(interval="minute"),
-        inputInterval="minute",
-        outputFactory=output_factory or get_edge_factory(interval="minute"),
-        outputInterval="minute",
+    derived.process_variation(
+        observatory=observatory,
+        interval="minute",
+        realtime_interval=realtime_interval,
+        update_limit=update_limit,
+    )
+    derived.sqdist_minute(
+        observatory=observatory,
+        data_type="variation",
+        statefile=sqdist_statefile,
+        realtime_interval=realtime_interval,
     )
-    # sqdist is stateful, use run
-    controller.run(
-        observatory=(observatory,),
-        output_observatory=(observatory,),
-        starttime=starttime,
-        endtime=endtime,
-        input_channels=("X", "Y", "Z", "F"),
-        output_channels=("MDT", "MSQ", "MSV"),
-        realtime=realtime_interval,
-        rename_output_channel=(("H_Dist", "MDT"), ("H_SQ", "MSQ"), ("H_SV", "MSV")),
-        update_limit=10,
+    pcdcp.legacy(
+        observatory=observatory,
+        data_type="variation",
+        realtime_interval=realtime_interval,
+        update_limit=update_limit,
     )
diff --git a/geomagio/processing/obsrio.py b/geomagio/processing/obsrio.py
index bf0acf2b81687c6a3f590e6b4cd8b902f4730aa3..d0019418531390c281e87a358de67b51e297d761 100644
--- a/geomagio/processing/obsrio.py
+++ b/geomagio/processing/obsrio.py
@@ -1,228 +1,108 @@
-from typing import Optional
+from enum import Enum
+from typing import Literal, List, Optional
 
 import typer
 
-from ..algorithm import Algorithm, FilterAlgorithm
-from ..edge import EdgeFactory, MiniSeedFactory
+from ..algorithm import FilterAlgorithm
 from ..Controller import (
     Controller,
     get_realtime_interval,
 )
 from ..TimeseriesFactory import TimeseriesFactory
-from .factory import get_edge_factory, get_miniseed_factory
+from .factory import get_miniseed_factory, get_edge_factory
+from .pcdcp import copy_channels
 
 
-def main():
-    typer.run(obsrio_filter)
-
-
-def obsrio_filter(
-    interval: str,
-    observatory: str,
-    input_factory: Optional[str] = None,
-    host: str = "127.0.0.1",
-    port: str = 2061,
-    output_factory: Optional[str] = None,
-    output_port: int = typer.Option(
-        2061, help="Port where output factory writes data."
-    ),
-    output_read_port: int = typer.Option(
-        2061, help="Port where output factory reads data"
-    ),
-    realtime_interval: int = 600,
-    update_limit: int = 10,
-):
-    if interval == "realtime":
-        filter_realtime(
-            observatory=observatory,
-            input_factory=input_factory,
-            host=host,
-            port=port,
-            output_factory=output_factory,
-            output_port=output_port,
-            output_read_port=output_read_port,
-            realtime_interval=realtime_interval,
-            update_limit=update_limit,
-        )
-    elif interval in ["hour", "day"]:
-        input_factory = EdgeFactory(host=host, port=port)
-        output_factory = MiniSeedFactory(
-            host=host, port=output_read_port, write_port=output_port
-        )
-        if interval == "hour":
-            obsrio_hour(
-                observatory=observatory,
-                input_factory=input_factory,
-                output_factory=output_factory,
-                realtime_interval=realtime_interval,
-                update_limit=update_limit,
-            )
-        elif interval == "day":
-            obsrio_day(
-                observatory=observatory,
-                input_factory=input_factory,
-                output_factory=output_factory,
-                realtime_interval=realtime_interval,
-                update_limit=update_limit,
-            )
-    else:
-        raise ValueError("Invalid interval")
-
-
-def filter_realtime(
-    observatory: str,
-    input_factory: Optional[str] = None,
-    host: str = "127.0.0.1",
-    port: str = 2061,
-    output_factory: Optional[str] = None,
-    output_port: int = typer.Option(
-        2061, help="Port where output factory writes data."
-    ),
-    output_read_port: int = typer.Option(
-        2061, help="Port where output factory reads data"
-    ),
-    realtime_interval: int = 600,
-    update_limit: int = 10,
-):
-    """Filter 10Hz miniseed, 1 second, one minute, and temperature data.
-    Defaults set for realtime processing; can also be implemented to update legacy data"""
-    if input_factory == "miniseed":
-        input_factory = MiniSeedFactory(host=host, port=port)
-    elif input_factory == "edge":
-        input_factory = EdgeFactory(host=host, port=port)
-    if output_factory == "miniseed":
-        output_factory = MiniSeedFactory(
-            host=host, port=output_read_port, write_port=output_port
-        )
-    elif output_factory == "edge":
-        output_factory = EdgeFactory(
-            host=host, port=output_read_port, write_port=output_port
-        )
-
-    obsrio_tenhertz(
-        observatory=observatory,
-        input_factory=input_factory,
-        output_factory=output_factory,
-        realtime_interval=realtime_interval,
-        update_limit=update_limit,
-    )
-    obsrio_second(
-        observatory=observatory,
-        input_factory=input_factory,
-        output_factory=output_factory,
-        realtime_interval=realtime_interval,
-        update_limit=update_limit,
-    )
-    obsrio_minute(
-        observatory=observatory,
-        input_factory=input_factory,
-        output_factory=output_factory,
-        realtime_interval=realtime_interval,
-        update_limit=update_limit,
-    )
-    obsrio_temperatures(
-        observatory=observatory,
-        input_factory=input_factory,
-        output_factory=output_factory,
-        realtime_interval=realtime_interval,
-        update_limit=update_limit,
-    )
-
-
-def obsrio_day(
+def filter_day(
     observatory: str,
+    data_type: Literal["adjusted", "variation"],
+    channels: List[str],
     input_factory: Optional[TimeseriesFactory] = None,
     output_factory: Optional[TimeseriesFactory] = None,
     realtime_interval: int = 86400,
     update_limit: int = 7,
 ):
-    """Filter 1 second edge H,E,Z,F to 1 day miniseed U,V,W,F."""
+    """Filter 1 minute miniseed to 1 day miniseed"""
     starttime, endtime = get_realtime_interval(realtime_interval)
     controller = Controller(
-        inputFactory=input_factory or get_edge_factory(),
+        inputFactory=input_factory or get_miniseed_factory(data_type=data_type),
         inputInterval="minute",
-        outputFactory=output_factory or get_miniseed_factory(),
+        outputFactory=output_factory or get_miniseed_factory(data_type=data_type),
         outputInterval="day",
     )
-    renames = {"H": "U", "E": "V", "Z": "W", "F": "F"}
-    for input_channel in renames.keys():
-        output_channel = renames[input_channel]
+    for channel in channels:
         controller.run_as_update(
             algorithm=FilterAlgorithm(
                 input_sample_period=60.0,
                 output_sample_period=86400.0,
-                inchannels=(input_channel,),
-                outchannels=(output_channel,),
+                inchannels=(channel,),
+                outchannels=(channel,),
             ),
             observatory=(observatory,),
             output_observatory=(observatory,),
             starttime=starttime,
             endtime=endtime,
-            input_channels=(input_channel,),
-            output_channels=(output_channel,),
+            input_channels=(channel,),
+            output_channels=(channel,),
             realtime=realtime_interval,
-            rename_output_channel=((input_channel, output_channel),),
             update_limit=update_limit,
         )
 
 
-def obsrio_hour(
+def filter_hour(
     observatory: str,
+    data_type: Literal["adjusted", "variation"],
+    channels: List[str],
     input_factory: Optional[TimeseriesFactory] = None,
     output_factory: Optional[TimeseriesFactory] = None,
     realtime_interval: int = 600,
     update_limit: int = 10,
 ):
-    """Filter 1 second edge H,E,Z,F to 1 hour miniseed U,V,W,F."""
+    """Filter 1 minute miniseed to 1 hour miniseed"""
     starttime, endtime = get_realtime_interval(realtime_interval)
     controller = Controller(
-        inputFactory=input_factory or get_edge_factory(),
+        inputFactory=input_factory or get_miniseed_factory(data_type=data_type),
         inputInterval="minute",
-        outputFactory=output_factory or get_miniseed_factory(),
+        outputFactory=output_factory or get_miniseed_factory(data_type=data_type),
         outputInterval="hour",
     )
-    renames = {"H": "U", "E": "V", "Z": "W", "F": "F"}
-    for input_channel in renames.keys():
-        output_channel = renames[input_channel]
+    for channel in channels:
         controller.run_as_update(
             algorithm=FilterAlgorithm(
                 input_sample_period=60.0,
                 output_sample_period=3600.0,
-                inchannels=(input_channel,),
-                outchannels=(output_channel,),
+                inchannels=(channel,),
+                outchannels=(channel,),
             ),
             observatory=(observatory,),
             output_observatory=(observatory,),
             starttime=starttime,
             endtime=endtime,
-            input_channels=(input_channel,),
-            output_channels=(output_channel,),
+            input_channels=(channel,),
+            output_channels=(channel,),
             realtime=realtime_interval,
-            rename_output_channel=((input_channel, output_channel),),
             update_limit=update_limit,
         )
 
 
-def obsrio_minute(
+def filter_minute(
     observatory: str,
+    channels: List[str],
+    data_type: Literal["adjusted", "variation"],
     input_factory: Optional[TimeseriesFactory] = None,
     output_factory: Optional[TimeseriesFactory] = None,
     realtime_interval: int = 600,
     update_limit: int = 10,
 ):
-    """Filter 1Hz legacy H,E,Z,F to 1 minute legacy.
-
-    Should be called after obsrio_second() and obsrio_tenhertz(),
-    which populate 1Hz legacy H,E,Z,F.
-    """
+    """Filter 1Hz miniseed to 1 minute miniseed"""
     starttime, endtime = get_realtime_interval(realtime_interval)
     controller = Controller(
-        inputFactory=input_factory or get_edge_factory(),
+        inputFactory=input_factory or get_miniseed_factory(data_type=data_type),
         inputInterval="second",
-        outputFactory=output_factory or get_edge_factory(),
+        outputFactory=output_factory or get_miniseed_factory(data_type=data_type),
         outputInterval="minute",
     )
-    for channel in ["H", "E", "Z", "F"]:
+    for channel in channels:
         controller.run_as_update(
             algorithm=FilterAlgorithm(
                 input_sample_period=1,
@@ -241,45 +121,19 @@ def obsrio_minute(
         )
 
 
-def obsrio_second(
+def filter_temperatures(
     observatory: str,
     input_factory: Optional[TimeseriesFactory] = None,
     output_factory: Optional[TimeseriesFactory] = None,
     realtime_interval: int = 600,
     update_limit: int = 10,
 ):
-    """Copy 1Hz miniseed F to 1Hz legacy F."""
-    starttime, endtime = get_realtime_interval(realtime_interval)
-    controller = Controller(
-        algorithm=Algorithm(inchannels=("F",), outchannels=("F",)),
-        inputFactory=input_factory or get_miniseed_factory(),
-        outputFactory=output_factory or get_edge_factory(),
-    )
-    controller.run_as_update(
-        observatory=(observatory,),
-        output_observatory=(observatory,),
-        starttime=starttime,
-        endtime=endtime,
-        input_channels=("F",),
-        output_channels=("F",),
-        realtime=realtime_interval,
-        update_limit=update_limit,
-    )
-
-
-def obsrio_temperatures(
-    observatory: str,
-    input_factory: Optional[TimeseriesFactory] = None,
-    output_factory: Optional[TimeseriesFactory] = None,
-    realtime_interval: int = 600,
-    update_limit: int = 10,
-):
-    """Filter temperatures 1Hz miniseed (LK1-4) to 1 minute legacy (UK1-4)."""
+    """Filter temperatures 1Hz miniseed (LK1-4) to 1 minute miniseed (UK1-4)."""
     starttime, endtime = get_realtime_interval(realtime_interval)
     controller = Controller(
         inputFactory=input_factory or get_miniseed_factory(),
         inputInterval="second",
-        outputFactory=output_factory or get_edge_factory(),
+        outputFactory=output_factory or get_miniseed_factory(),
         outputInterval="minute",
     )
     renames = {"LK1": "UK1", "LK2": "UK2", "LK3": "UK3", "LK4": "UK4"}
@@ -304,38 +158,69 @@ def obsrio_temperatures(
         )
 
 
-def obsrio_tenhertz(
+def filter_tenhertz(
     observatory: str,
     input_factory: Optional[TimeseriesFactory] = None,
     output_factory: Optional[TimeseriesFactory] = None,
     realtime_interval: int = 600,
     update_limit: int = 10,
 ):
-    """Filter 10Hz miniseed U,V,W to 1Hz legacy H,E,Z."""
+    """Filter 10Hz miniseed U,V,W to 1Hz miniseed U,V,W"""
     starttime, endtime = get_realtime_interval(realtime_interval)
     controller = Controller(
-        inputFactory=input_factory or get_miniseed_factory(),
+        inputFactory=input_factory
+        or get_miniseed_factory(convert_channels=("U", "V", "W")),
         inputInterval="tenhertz",
-        outputFactory=output_factory or get_edge_factory(),
+        outputFactory=output_factory or get_miniseed_factory(),
         outputInterval="second",
     )
-    renames = {"U": "H", "V": "E", "W": "Z"}
-    for input_channel in renames.keys():
-        output_channel = renames[input_channel]
+    for channel in ["U", "V", "W"]:
         controller.run_as_update(
             algorithm=FilterAlgorithm(
                 input_sample_period=0.1,
                 output_sample_period=1,
-                inchannels=(input_channel,),
-                outchannels=(output_channel,),
+                inchannels=(channel,),
+                outchannels=(channel,),
             ),
             observatory=(observatory,),
             output_observatory=(observatory,),
             starttime=starttime,
             endtime=endtime,
-            input_channels=(input_channel,),
-            output_channels=(output_channel,),
+            input_channels=(channel,),
+            output_channels=(channel,),
             realtime=realtime_interval,
-            rename_output_channel=((input_channel, output_channel),),
             update_limit=update_limit,
         )
+
+
+def prepare(observatory: str, realtime_interval: int = 600, update_limit: int = 10):
+    filter_tenhertz(
+        observatory=observatory,
+        realtime_interval=realtime_interval,
+        update_limit=update_limit,
+    )
+    filter_temperatures(
+        observatory=observatory,
+        realtime_interval=realtime_interval,
+        update_limit=update_limit,
+    )
+    copy_channels(
+        observatory=observatory,
+        input_channels=["UK1", "UK2", "UK3", "UK4"],
+        output_channels=["UK1", "UK2", "UK3", "UK4"],
+        interval="minute",
+        input_factory=get_miniseed_factory(),
+        output_factory=get_edge_factory(),
+        realtime_interval=realtime_interval,
+        update_limit=update_limit,
+    )
+    copy_channels(
+        observatory=observatory,
+        input_channels=["W"],
+        output_channels=["Z"],
+        interval="second",
+        input_factory=get_miniseed_factory(),
+        output_factory=get_miniseed_factory(),
+        realtime_interval=realtime_interval,
+        update_limit=update_limit,
+    )
diff --git a/geomagio/processing/pcdcp.py b/geomagio/processing/pcdcp.py
new file mode 100644
index 0000000000000000000000000000000000000000..8374f6d68cd2bdd59a8c5c7b96ff6d083f97ae44
--- /dev/null
+++ b/geomagio/processing/pcdcp.py
@@ -0,0 +1,141 @@
+from typing import List, Literal, Optional
+
+from ..algorithm import Algorithm
+from ..Controller import Controller, get_realtime_interval
+from .. import TimeseriesFactory
+from .factory import get_edge_factory, get_miniseed_factory
+
+
+def prepare(
+    observatory: str,
+    input_factory: Optional[TimeseriesFactory] = None,
+    output_factory: Optional[TimeseriesFactory] = None,
+    realtime_interval: int = 600,
+    update_limit: int = 10,
+):
+    """Copies 1Hz edge HEZF to 1Hz miniseed UVWF
+    UK1-4 edge to UK1-4 miniseed"""
+    input_factory = input_factory or get_edge_factory()
+    output_factory = output_factory or get_miniseed_factory()
+    copy_channels(
+        observatory=observatory,
+        input_channels=["H", "E", "Z", "F"],
+        output_channels=["U", "V", "W", "F"],
+        interval="second",
+        input_factory=input_factory,
+        output_factory=output_factory,
+        realtime_interval=realtime_interval,
+        update_limit=update_limit,
+    )
+    copy_channels(
+        observatory=observatory,
+        input_channels=["W"],
+        output_channels=["Z"],
+        interval="second",
+        input_factory=output_factory,
+        output_factory=output_factory,
+        realtime_interval=realtime_interval,
+        update_limit=update_limit,
+    )
+    copy_channels(
+        observatory=observatory,
+        input_channels=["UK1", "UK2", "UK3", "UK4"],
+        output_channels=["UK1", "UK2", "UK3", "UK4"],
+        interval="minute",
+        input_factory=input_factory,
+        output_factory=output_factory,
+        realtime_interval=realtime_interval,
+        update_limit=update_limit,
+    )
+
+
+def legacy(
+    observatory: str,
+    data_type: Literal["adjusted", "variation"],
+    realtime_interval: int = 600,
+    update_limit: int = 10,
+):
+    """copy filtered/derived miniseed channels to edge"""
+    input_factory = get_miniseed_factory(data_type=data_type)
+    output_factory = get_edge_factory(data_type=data_type)
+    seconds_channels = [
+        "X",
+        "Y",
+        "D",
+        "G",
+    ]
+    minutes_input_channels = [
+        "X",
+        "Y",
+        "D",
+        "G",
+        "H_SQ",
+        "H_SV",
+        "H_Dist",
+    ]
+    minutes_output_channels = [
+        "X",
+        "Y",
+        "D",
+        "G",
+        "MSQ",
+        "MSV",
+        "MDT",
+    ]
+    if data_type == "adjusted":
+        seconds_channels.extend(["H", "F", "Z"])
+        minutes_input_channels.extend(["H", "F", "Z"])
+        minutes_output_channels.extend(["H", "F", "Z"])
+    copy_channels(
+        observatory=observatory,
+        input_channels=seconds_channels,
+        output_channels=seconds_channels,
+        interval="second",
+        input_factory=input_factory,
+        output_factory=output_factory,
+        realtime_interval=realtime_interval,
+        update_limit=update_limit,
+    )
+    copy_channels(
+        observatory=observatory,
+        input_channels=minutes_input_channels,
+        output_channels=minutes_output_channels,
+        interval="minute",
+        input_factory=input_factory,
+        output_factory=output_factory,
+        realtime_interval=realtime_interval,
+        update_limit=update_limit,
+    )
+
+
+def copy_channels(
+    observatory: str,
+    input_channels: List[str],
+    output_channels: List[str],
+    interval: str,
+    input_factory: TimeseriesFactory,
+    output_factory: TimeseriesFactory,
+    realtime_interval: int = 600,
+    update_limit: int = 10,
+):
+    """Copy channels between edge and miniseed format"""
+    starttime, endtime = get_realtime_interval(realtime_interval)
+    controller = Controller(
+        algorithm=Algorithm(),
+        inputFactory=input_factory,
+        outputFactory=output_factory,
+        inputInterval=interval,
+        outputInterval=interval,
+    )
+    for input_channel, output_channel in zip(input_channels, output_channels):
+        controller.run_as_update(
+            observatory=(observatory,),
+            output_observatory=(observatory,),
+            starttime=starttime,
+            endtime=endtime,
+            input_channels=(input_channel,),
+            output_channels=(output_channel,),
+            rename_output_channel=((input_channel, output_channel),),
+            realtime=realtime_interval,
+            update_limit=update_limit,
+        )
diff --git a/setup.py b/setup.py
index 9a9c5ace7e577e308272665250636eeb2e1027af..155431c5fbaabdae618116d07373846b83a9477f 100644
--- a/setup.py
+++ b/setup.py
@@ -28,7 +28,7 @@ setuptools.setup(
             "generate-matrix=geomagio.processing.adjusted:main",
             "geomag-metadata=geomagio.metadata.main:main",
             "magproc-prepfiles=geomagio.processing.magproc:main",
-            "obsrio-filter=geomagio.processing.obsrio:main",
+            "observatory=geomagio.processing.observatory:main",
         ],
     },
 )
diff --git a/test/StreamConverter_test.py b/test/StreamConverter_test.py
index 0c8b4207ac380f9d27efb5c526d24f9703dcec78..4421411516715a3bf73662d0dba8c0048550b451 100644
--- a/test/StreamConverter_test.py
+++ b/test/StreamConverter_test.py
@@ -63,16 +63,16 @@ def test_get_geo_from_obs():
     """StreamConverter_test.test_get_geo_from_obs()
 
     The observatory stream containing the observatory traces
-    ''h'', ''d'' or ''e'', ''z'', and ''f'' converts to the geographic
+    ''u'', ''d'' or ''v'', ''w'', and ''f'' converts to the geographic
     stream containing the traces ''x'', ''y'', ''z'', and ''f''
     """
     obs = obspy.core.Stream()
 
-    # 1) Call get_geo_from_obs using equal h, e streams with a decbas of 0
+    # 1) Call get_geo_from_obs using equal u, v streams with a decbas of 0
     #   the geographic stream values X, Y will be the same.
-    obs += __create_trace("H", [1])
-    obs += __create_trace("E", [1])
-    obs += __create_trace("Z", [1])
+    obs += __create_trace("U", [1])
+    obs += __create_trace("V", [1])
+    obs += __create_trace("W", [1])
     obs += __create_trace("F", [1])
     geo = StreamConverter.get_geo_from_obs(obs)
     X = geo.select(channel="X")[0].data
@@ -81,13 +81,13 @@ def test_get_geo_from_obs():
     assert_almost_equal(Y[0], 1, 9, "Expect Y to almost equal 1", True)
 
     # 2) Call get_geo_from_obs using a decbas of 15 degrees, and streams
-    #   with H = [cos(15), cos(30)], and E = [sin(15), sin(30)].
+    #   with U = [cos(15), cos(30)], and V = [sin(15), sin(30)].
     #   Expect streams of X = [cos(30), cos(45)] and Y = sin(30), sin(45)
     obs = obspy.core.Stream()
     DECBAS = 15 * D2I
-    obs += __create_trace("H", [cos(15 * D2R), cos(30 * D2R)], DECBAS)
-    obs += __create_trace("E", [sin(15 * D2R), sin(30 * D2R)], DECBAS)
-    obs += __create_trace("Z", [1, 1], DECBAS)
+    obs += __create_trace("U", [cos(15 * D2R), cos(30 * D2R)], DECBAS)
+    obs += __create_trace("V", [sin(15 * D2R), sin(30 * D2R)], DECBAS)
+    obs += __create_trace("W", [1, 1], DECBAS)
     obs += __create_trace("F", [1, 1], DECBAS)
     geo = StreamConverter.get_geo_from_obs(obs)
     X = geo.select(channel="X")[0].data
@@ -137,18 +137,18 @@ def test_get_mag_from_geo():
 def test_get_mag_from_obs():
     """StreamConverter_test.test_get_mag_from_obs()
 
-    The observatory stream containing the traces ''h'', ''e'' or ''d'',
-        ''z'' and ''f''
+    The observatory stream containing the traces ''u'', ''v'' or ''d'',
+        ''w'' and ''f''
     """
     obs = obspy.core.Stream()
 
-    # Call get_mag_from_obs using a DECBAS of 15 degrees, a H stream of
-    #   [cos(15), cos(30)] and a E stream of [sin(15), sin(30)].
+    # Call get_mag_from_obs using a DECBAS of 15 degrees, a U stream of
+    #   [cos(15), cos(30)] and a V stream of [sin(15), sin(30)].
     #   Expect a H stream of [1, 1] and a D stream of [30 degrees, 45 degrees]
     DECBAS = 15 * D2I
-    obs += __create_trace("H", [cos(15 * D2R), cos(30 * D2R)], DECBAS)
-    obs += __create_trace("E", [sin(15 * D2R), sin(30 * D2R)], DECBAS)
-    obs += __create_trace("Z", [1, 1], DECBAS)
+    obs += __create_trace("U", [cos(15 * D2R), cos(30 * D2R)], DECBAS)
+    obs += __create_trace("V", [sin(15 * D2R), sin(30 * D2R)], DECBAS)
+    obs += __create_trace("W", [1, 1], DECBAS)
     obs += __create_trace("F", [1, 1], DECBAS)
     mag = StreamConverter.get_mag_from_obs(obs)
     H = mag.select(channel="H")[0].data
@@ -164,35 +164,35 @@ def test_get_obs_from_geo():
 
     The geographic stream containing the traces ''x'', ''y'', ''z'', and
     ''f'' converts to the observatory stream containing the traces
-    ''h'', ''d'' or ''e'', ''z'', and ''f''.
+    ''u'', ''d'' or ''v'', ''w'', and ''f''.
     """
     geo = obspy.core.Stream()
 
     # Call get_geo_from_obs using a decbas of 15, a X stream of
     #   [cos(30), cos(45)], and a Y stream of [sin(30), sin(45)].
-    #   Expect a H stream of [cos(15), cos(30)] and a
-    #   E stream of [sin(15), sin(30)]
+    #   Expect a U stream of [cos(15), cos(30)] and a
+    #   V stream of [sin(15), sin(30)]
     DECBAS = 15 * D2I
     geo += __create_trace("X", [cos(30 * D2R), cos(45 * D2R)], DECBAS)
     geo += __create_trace("Y", [sin(30 * D2R), sin(45 * D2R)], DECBAS)
     geo += __create_trace("Z", [1, 1], DECBAS)
     geo += __create_trace("F", [1, 1], DECBAS)
     obs = StreamConverter.get_obs_from_geo(geo, True)
-    H = obs.select(channel="H")[0].data
-    E = obs.select(channel="E")[0].data
+    U = obs.select(channel="U")[0].data
+    V = obs.select(channel="V")[0].data
     D = obs.select(channel="D")[0].data
     assert_almost_equal(
-        H,
+        U,
         [cos(15 * D2R), cos(30 * D2R)],
         9,
-        "Expect H to equal [cos(15), cos(30)]",
+        "Expect U to equal [cos(15), cos(30)]",
         True,
     )
     assert_almost_equal(
-        E,
+        V,
         [sin(15 * D2R), sin(30 * D2R)],
         9,
-        "Expect E to equal [sin(15), sin(30)",
+        "Expect V to equal [sin(15), sin(30)",
         True,
     )
     assert_almost_equal(
@@ -205,38 +205,38 @@ def test_get_obs_from_mag():
 
     The magnetic stream containing the traces ''h'', ''d'', ''z'', and ''f''
         converts to the observatory stream containing the traces
-        ''h'', ''e'' and/or ''d'', ''z'', and ''f''
+        ''u'', ''v'' and/or ''d'', ''w'', and ''f''
     """
     mag = obspy.core.Stream()
 
     # Call get_obs_from_mag using a decbas of 15, a H stream of [1,1],
-    #   and a D stream of [30 degrees, 45 degrees]. Expect a H stream
+    #   and a D stream of [30 degrees, 45 degrees]. Expect a U stream
     #   of [cos(15), cos(30)], a D stream of [30 degrees, 45 degrees],
-    #   and a E stream of [sin(15), sin(30)]
+    #   and a V stream of [sin(15), sin(30)]
     DECBAS = 15 * D2I
     mag += __create_trace("H", [1, 1], DECBAS)
     mag += __create_trace("D", [30 * D2R, 45 * D2R], DECBAS)
     mag += __create_trace("Z", [1, 1], DECBAS)
     mag += __create_trace("F", [1, 1], DECBAS)
     obs = StreamConverter.get_obs_from_mag(mag, True)
-    H = obs.select(channel="H")[0].data
+    U = obs.select(channel="U")[0].data
     D = obs.select(channel="D")[0].data
-    E = obs.select(channel="E")[0].data
+    V = obs.select(channel="V")[0].data
     assert_almost_equal(
-        H,
+        U,
         [cos(15 * D2R), cos(30 * D2R)],
         9,
-        "Expect H to equal [cos(15), cos(30)",
+        "Expect U to equal [cos(15), cos(30)",
         True,
     )
     assert_almost_equal(
         D, [15 * D2R, 30 * D2R], 9, "Expect D to equal [15 degrees, 30 degrees", True
     )
     assert_almost_equal(
-        E,
+        V,
         [sin(15 * D2R), sin(30 * D2R)],
         9,
-        "Expect E to equal [sin(15), sin(30)",
+        "Expect V to equal [sin(15), sin(30)",
         True,
     )
 
@@ -244,19 +244,19 @@ def test_get_obs_from_mag():
 def test_get_obs_from_obs():
     """StreamConverter_test.test_get_obs_from_obs()
 
-    The observatory stream can contain either ''d'' or ''e'' depending
+    The observatory stream can contain either ''d'' or ''v'' depending
     on it's source. get_obs_from_obs will return either or both as part
     of the obs Stream.
     """
 
-    # 1) Call get_obs_from_obs using a decbas of 15, a H stream of
-    #   [cos(15), cos(30)], and a E stream of [sin(15), sin(30)].
+    # 1) Call get_obs_from_obs using a decbas of 15, a U stream of
+    #   [cos(15), cos(30)], and a V stream of [sin(15), sin(30)].
     #   Expect a D stream of [15 degrees, 30 degrees]
     obs_e = obspy.core.Stream()
     DECBAS = 15 * D2I
-    obs_e += __create_trace("H", [cos(15 * D2R), cos(30 * D2R)], DECBAS)
-    obs_e += __create_trace("E", [sin(15 * D2R), sin(30 * D2R)], DECBAS)
-    obs_e += __create_trace("Z", [1, 1], DECBAS)
+    obs_e += __create_trace("U", [cos(15 * D2R), cos(30 * D2R)], DECBAS)
+    obs_e += __create_trace("V", [sin(15 * D2R), sin(30 * D2R)], DECBAS)
+    obs_e += __create_trace("W", [1, 1], DECBAS)
     obs_e += __create_trace("F", [1, 1], DECBAS)
     obs_D = StreamConverter.get_obs_from_obs(obs_e, False, True)
     d = obs_D.select(channel="D")[0].data
@@ -264,21 +264,21 @@ def test_get_obs_from_obs():
         d, [15 * D2R, 30 * D2R], 9, "Expect D to equal [15 degrees, 30 degrees]", True
     )
 
-    # 2) Call get_obs_from_obs using a decbase of 15 degrees, a H stream of
+    # 2) Call get_obs_from_obs using a decbase of 15 degrees, a U stream of
     #   [cos(15), cos(30)], and a D stream of [15, 30].
     #   Expect a D stream of [sin(15), sin(30)]
     obs_d = obspy.core.Stream()
-    obs_d += __create_trace("H", [cos(15 * D2R), cos(30 * D2R)], DECBAS)
+    obs_d += __create_trace("U", [cos(15 * D2R), cos(30 * D2R)], DECBAS)
     obs_d += __create_trace("D", [15 * D2R, 30 * D2R], DECBAS)
-    obs_d += __create_trace("Z", [1, 1], DECBAS)
+    obs_d += __create_trace("W", [1, 1], DECBAS)
     obs_d += __create_trace("F", [1, 1], DECBAS)
-    obs_E = StreamConverter.get_obs_from_obs(obs_d, True, False)
-    e = obs_E.select(channel="E")[0].data
+    obs_V = StreamConverter.get_obs_from_obs(obs_d, True, False)
+    v = obs_V.select(channel="V")[0].data
     assert_almost_equal(
-        e,
+        v,
         [sin(15 * D2R), sin(30 * D2R)],
         9,
-        "Expect E to equal [sin(15), sin(30)",
+        "Expect V to equal [sin(15), sin(30)",
         True,
     )
 
@@ -298,13 +298,13 @@ def test_verification_data():
     DECBAS = 552.7
     obs_v = obspy.core.Stream()
     obs_v += __create_trace(
-        "H", [20889.55, 20889.57, 20889.74, 20889.86, 20889.91, 20889.81], DECBAS
+        "U", [20889.55, 20889.57, 20889.74, 20889.86, 20889.91, 20889.81], DECBAS
     )
     obs_v += __create_trace(
-        "E", [-21.10, -20.89, -20.72, -20.57, -20.39, -20.12], DECBAS
+        "V", [-21.10, -20.89, -20.72, -20.57, -20.39, -20.12], DECBAS
     )
     obs_v += __create_trace(
-        "Z", [47565.29, 47565.34, 47565.39, 47565.45, 47565.51, 47565.54], DECBAS
+        "W", [47565.29, 47565.34, 47565.39, 47565.45, 47565.51, 47565.54], DECBAS
     )
     obs_v += __create_trace(
         "F", [52485.77, 52485.84, 52485.94, 52486.06, 52486.11, 52486.10], DECBAS
diff --git a/test/algorithm_test/AdjustedAlgorithm_test.py b/test/algorithm_test/AdjustedAlgorithm_test.py
index 220d498c6f1bf6a65e59243523cd73789b6dc95e..f06b4405181222ec7730a5ed02d8ec5fd1f226ef 100644
--- a/test/algorithm_test/AdjustedAlgorithm_test.py
+++ b/test/algorithm_test/AdjustedAlgorithm_test.py
@@ -87,8 +87,8 @@ def test_process_reverse_polarity_AdjustedMatrix():
             ],
             pier_correction=-22,
         ),
-        inchannels=["H", "E"],
-        outchannels=["H", "E"],
+        inchannels=["U", "V"],
+        outchannels=["U", "V"],
     )
 
     # load boulder May 20 files from /etc/ directory
@@ -101,7 +101,7 @@ def test_process_reverse_polarity_AdjustedMatrix():
     adjusted = a.process(raw)
 
     assert_streams_almost_equal(
-        adjusted=adjusted, expected=expected, channels=["H", "E"]
+        adjusted=adjusted, expected=expected, channels=["U", "V"]
     )
 
 
@@ -141,8 +141,8 @@ def test_process_reverse_polarity_statefile():
     # load adjusted data transform matrix and pier correction
     a = AdjustedAlgorithm(
         statefile="etc/adjusted/adjbou_state_HE_.json",
-        inchannels=["H", "E"],
-        outchannels=["H", "E"],
+        inchannels=["U", "V"],
+        outchannels=["U", "V"],
     )
 
     # load boulder May 20 files from /etc/ directory
@@ -155,7 +155,7 @@ def test_process_reverse_polarity_statefile():
     adjusted = a.process(raw)
 
     assert_streams_almost_equal(
-        adjusted=adjusted, expected=expected, channels=["H", "E"]
+        adjusted=adjusted, expected=expected, channels=["U", "V"]
     )
 
 
diff --git a/test/algorithm_test/XYZAlgorithm_test.py b/test/algorithm_test/XYZAlgorithm_test.py
index 67be143a880a3ed11b5de59c0d711e0773c205d7..bc1edb16a3160b12512fca5f302e375a6276055b 100644
--- a/test/algorithm_test/XYZAlgorithm_test.py
+++ b/test/algorithm_test/XYZAlgorithm_test.py
@@ -13,9 +13,9 @@ def test_xyzalgorithm_process():
     """
     algorithm = XYZAlgorithm("obs", "geo")
     timeseries = Stream()
-    timeseries += __create_trace("H", [1, 1])
-    timeseries += __create_trace("E", [1, 1])
-    timeseries += __create_trace("Z", [1, 1])
+    timeseries += __create_trace("U", [1, 1])
+    timeseries += __create_trace("V", [1, 1])
+    timeseries += __create_trace("W", [1, 1])
     timeseries += __create_trace("F", [1, 1])
     outputstream = algorithm.process(timeseries)
     assert_equal(outputstream[0].stats.channel, "X")
@@ -28,7 +28,7 @@ def test_xyzalgorithm_channels():
     informat/outformat during instantiation.
     """
     algorithm = XYZAlgorithm("obs", "geo")
-    inchannels = ["H", "E", "Z", "F"]
+    inchannels = ["U", "V", "W", "F"]
     outchannels = ["X", "Y", "Z", "F"]
     assert_equal(algorithm.get_input_channels(), inchannels)
     assert_equal(algorithm.get_output_channels(), outchannels)
@@ -38,13 +38,13 @@ def test_xyzalgorithm_limited_channels():
     """XYZAlgorithm_test.test_xyzalgorithm_limited_channels()
 
     confirms that only the required channels are necessary for processing
-    ie. 'H' and 'E' are only needed to get 'X' and 'Y' without 'Z' or 'F'
+    ie. 'U' and 'V' are only needed to get 'X' and 'Y' without 'Z' or 'F'
     """
     algorithm = XYZAlgorithm("obs", "mag")
     count = 5
     timeseries = Stream()
-    timeseries += __create_trace("H", [2] * count)
-    timeseries += __create_trace("E", [3] * count)
+    timeseries += __create_trace("U", [2] * count)
+    timeseries += __create_trace("V", [3] * count)
     outstream = algorithm.process(timeseries)
     ds = outstream.select(channel="D")
     # there is 1 trace
@@ -66,14 +66,14 @@ def test_xyzalgorithm_uneccesary_channel_empty():
     """
     algorithm = XYZAlgorithm("obs", "mag")
     timeseries = Stream()
-    timeseries += __create_trace("H", [1, 1])
-    timeseries += __create_trace("E", [1, 1])
-    timeseries += __create_trace("Z", [1, np.NaN])
+    timeseries += __create_trace("U", [1, 1])
+    timeseries += __create_trace("V", [1, 1])
+    timeseries += __create_trace("W", [1, np.NaN])
     timeseries += __create_trace("F", [np.NaN, np.NaN])
     outstream = algorithm.process(timeseries)
     assert_equal(
-        outstream.select(channel="Z")[0].data.all(),
-        timeseries.select(channel="Z")[0].data.all(),
+        outstream.select(channel="W")[0].data.all(),
+        timeseries.select(channel="W")[0].data.all(),
     )
     assert_equal(
         outstream.select(channel="F")[0].data.all(),
diff --git a/test/edge_test/EdgeFactory_test.py b/test/edge_test/EdgeFactory_test.py
index fca38a8c8df6fb8cd08a7717ff2114f5e7f5daf3..599299d1fd3423d66cdc9a9cd66b98aff6e774f1 100644
--- a/test/edge_test/EdgeFactory_test.py
+++ b/test/edge_test/EdgeFactory_test.py
@@ -25,6 +25,9 @@ def test__get_edge_channel():
     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("", "U", "", "minute"), "MVH")
+    assert_equal(EdgeFactory()._get_edge_channel("", "V", "", "minute"), "MVE")
+    assert_equal(EdgeFactory()._get_edge_channel("", "W", "", "minute"), "MVZ")
     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")
diff --git a/test/edge_test/MiniSeedFactory_test.py b/test/edge_test/MiniSeedFactory_test.py
index a0c57869e7ff0ffcea37f7c6f5c98842347e2492..3a7fde61837a09d73485077f4db92b265f5c9303 100644
--- a/test/edge_test/MiniSeedFactory_test.py
+++ b/test/edge_test/MiniSeedFactory_test.py
@@ -98,7 +98,7 @@ def test__pre_process():
     processed = MiniSeedInputClient(host=None)._pre_process(stream=Stream(trace))
     assert len(processed) == 2
     for trace in processed:
-        assert trace.data.dtype == "float32"
+        assert trace.data.dtype == "float64"
         stats = trace.stats
         assert stats.npts == 86400
         assert stats.starttime.timestamp % 86400 == 0
@@ -110,7 +110,7 @@ def test__format_miniseed():
     buf = io.BytesIO()
     trace = __create_trace(numpy.arange((86400 * 2) + 1), channel="H")
     MiniSeedInputClient(host=None)._format_miniseed(stream=Stream(trace), buf=buf)
-    block_size = 512
+    block_size = 1024
     data = buf.getvalue()
     n_blocks = int(len(data) / block_size)
     assert n_blocks == 1516