From 2571fc3fa9803cdf1601531a7774e1d883306a83 Mon Sep 17 00:00:00 2001
From: pcain-usgs <pcain@usgs.gov>
Date: Wed, 7 Oct 2020 12:08:04 -0600
Subject: [PATCH] Update rounding to microseconds, move method, split tests

---
 geomagio/TimeseriesUtility.py         | 24 +++++++++++
 geomagio/edge/RawInputClient.py       | 26 ++++++------
 test/TimeseriesUtility_test.py        | 13 ++++++
 test/edge_test/RawInputClient_test.py | 60 +++++++++++++++++++--------
 4 files changed, 92 insertions(+), 31 deletions(-)

diff --git a/geomagio/TimeseriesUtility.py b/geomagio/TimeseriesUtility.py
index c40ba8609..9aa99a895 100644
--- a/geomagio/TimeseriesUtility.py
+++ b/geomagio/TimeseriesUtility.py
@@ -535,3 +535,27 @@ def pad_and_trim_trace(trace, starttime, endtime):
             trace.data = numpy.concatenate(
                 [trace.data, numpy.full(cnt, numpy.nan, dtype=numpy.float64)]
             )
+
+
+def round_usecs(time):
+    """Rounds residual microseconds to milliseconds.
+
+    Parameters
+    ----------
+    time: UTCDateTime
+        time containing microsecond values
+
+    Returns
+    ----------
+    time: UTCDateTime
+        time containing rounded(or non-rounded) microsecond values
+    """
+    usecs = time.microsecond / 1000
+    # round microseconds to nearest millisecond
+    rounded_usecs = int(round(usecs, 0)) * 1000
+    # reset microseconds to 0 at top of second, add second to input time
+    if rounded_usecs > 999000:
+        rounded_usecs = 0
+        time += 1
+    time.microsecond = rounded_usecs
+    return time
diff --git a/geomagio/edge/RawInputClient.py b/geomagio/edge/RawInputClient.py
index 7259c2798..e6483c7fa 100644
--- a/geomagio/edge/RawInputClient.py
+++ b/geomagio/edge/RawInputClient.py
@@ -6,6 +6,7 @@ import struct
 import sys
 from datetime import datetime
 from ..TimeseriesFactoryException import TimeseriesFactoryException
+from ..TimeseriesUtility import round_usecs
 import logging
 from obspy.core import UTCDateTime
 from time import sleep
@@ -415,7 +416,7 @@ class RawInputClient:
         """
         PARAMETERS
         ----------
-        time: UTCDateTime
+        input_time: UTCDateTime
             time of the first sample
 
         RETURNS
@@ -426,24 +427,21 @@ class RawInputClient:
             secs: int
             usecs: int
         """
-        usecs = time.microsecond / 100000
-        # round microseconds to nearest microsecond(whole number)
-        rounded_usecs = int(round(usecs, 0))
-        # add warning to log if residual microsecond values are received
-        if rounded_usecs != usecs:
+
+        # check for presence of residual microseconds
+        if (time.microsecond / 1000).is_integer() == False:
+            # create warning message when rounding is necessary
             logging.warning(
-                "residual microsecond values encountered, rounding to nearest microsecond"
+                "residual microsecond values encountered, rounding to nearest millisecond"
             )
-        # in the case that microseconds rounds up, account for next second
-        if rounded_usecs == 10:
-            time += 1
-            rounded_usecs = 0
-        # establish rest of date
+            # round residual microsecond values
+            time = round_usecs(time)
+
         yr = time.year
         doy = time.datetime.timetuple().tm_yday
         secs = time.hour * 3600 + time.minute * 60 + time.second
-
-        return (yr, doy, secs, rounded_usecs)
+        usecs = time.microsecond
+        return (yr, doy, secs, usecs)
 
     def _open_socket(self):
         """Open a socket
diff --git a/test/TimeseriesUtility_test.py b/test/TimeseriesUtility_test.py
index 827b0afec..512db7edf 100644
--- a/test/TimeseriesUtility_test.py
+++ b/test/TimeseriesUtility_test.py
@@ -449,6 +449,19 @@ def test_pad_and_trim_trace_fixfloat():
     )
 
 
+def test_round_usecs():
+    """TimeseriesUtility_test.test_round_usecs()
+    This tests whether microsecond values are rounded or
+    not depending on residual microsecond values
+    """
+    # test case with no residual microseconds
+    time = TimeseriesUtility.round_usecs(UTCDateTime("2020-10-07T00:00:00Z"))
+    assert_equal(time, UTCDateTime("2020-10-07T00:00:00Z"))
+    # test case with residual microseconds
+    time = TimeseriesUtility.round_usecs(UTCDateTime("2020-10-07T00:00:00.995600Z"))
+    assert_equal(time, UTCDateTime("2020-10-07T00:00:00.996000Z"))
+
+
 def _create_trace(data, channel, starttime, delta=60.0):
     stats = Stats()
     stats.channel = channel
diff --git a/test/edge_test/RawInputClient_test.py b/test/edge_test/RawInputClient_test.py
index 73f2e1984..ef21290ac 100644
--- a/test/edge_test/RawInputClient_test.py
+++ b/test/edge_test/RawInputClient_test.py
@@ -91,20 +91,46 @@ def test__get_time_values(caplog):
         location=location,
         network=network,
     )
-    # define expected date from _get_time_values
-    expected_time = UTCDateTime(2020, 10, 7, 0, 0, 0, 0)
-    # define date with residual microseconds
-    residual_time = UTCDateTime(2020, 10, 6, 23, 59, 59, 999999)
-    r_yr, r_doy, r_secs, r_usecs = client._get_time_values(residual_time)
-    # check if input microsecond value changes within function
-    message = caplog.messages[0]
-    assert_equal(
-        message,
-        "residual microsecond values encountered, rounding to nearest microsecond",
-    )
-    e_yr, e_doy, e_secs, e_usecs = client._get_time_values(expected_time)
-    # test if residual result matches expected result
-    assert_equal(e_yr, r_yr)
-    assert_equal(e_doy, r_doy)
-    assert_equal(e_secs, r_secs)
-    assert_equal(e_usecs, r_usecs)
+
+    # test rounding up of microsecond value
+    time = UTCDateTime("2020-10-07T00:00:15.196855Z")
+    yr, doy, secs, usecs = client._get_time_values(time)
+    assert_equal(yr, 2020)
+    assert_equal(doy, 281)
+    assert_equal(secs, 15)
+    assert_equal(usecs, 197000)
+    # test rounding down of microsecond value
+    time = UTCDateTime("2020-10-07T00:00:15.196455Z")
+    yr, doy, secs, usecs = client._get_time_values(time)
+    assert_equal(yr, 2020)
+    assert_equal(doy, 281)
+    assert_equal(secs, 15)
+    assert_equal(usecs, 196000)
+    # test top of second adjustment
+    time = UTCDateTime("2020-10-07T00:00:00.999999Z")
+    yr, doy, secs, usecs = client._get_time_values(time)
+    assert_equal(yr, 2020)
+    assert_equal(doy, 281)
+    assert_equal(secs, 1)
+    assert_equal(usecs, 0)
+    # test top of day adjustment
+    time = UTCDateTime("2020-10-07T23:59:59.999999Z")
+    yr, doy, secs, usecs = client._get_time_values(time)
+    assert_equal(yr, 2020)
+    assert_equal(doy, 282)
+    assert_equal(secs, 0)
+    assert_equal(usecs, 0)
+    # assert if previous 4 tests generate 4 warning messages
+    assert_equal(len(caplog.messages), 4)
+
+    # clear warnings from log
+    caplog.clear()
+    # test ideal case
+    time = UTCDateTime("2020-10-07T00:00:00.232000Z")
+    yr, doy, secs, usecs = client._get_time_values(time)
+    assert_equal(yr, 2020)
+    assert_equal(doy, 281)
+    assert_equal(secs, 0)
+    assert_equal(usecs, 232000)
+    # assert if previous test does not generate a warning message
+    assert_equal(len(caplog.messages), 0)
-- 
GitLab