From 3d3c80e4fc10bb038d2d922b2712cfd33886506e Mon Sep 17 00:00:00 2001 From: pcain-usgs <pcain@usgs.gov> Date: Tue, 6 Oct 2020 16:28:56 -0600 Subject: [PATCH 1/5] Round to nearest tenth of second --- geomagio/edge/RawInputClient.py | 17 ++++++++++-- test/edge_test/RawInputClient_test.py | 37 ++++++++++++++++++++++++++- 2 files changed, 51 insertions(+), 3 deletions(-) diff --git a/geomagio/edge/RawInputClient.py b/geomagio/edge/RawInputClient.py index f50a93cd2..7259c2798 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 +import logging from obspy.core import UTCDateTime from time import sleep @@ -425,12 +426,24 @@ 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: + logging.warning( + "residual microsecond values encountered, rounding to nearest microsecond" + ) + # in the case that microseconds rounds up, account for next second + if rounded_usecs == 10: + time += 1 + rounded_usecs = 0 + # establish rest of date yr = time.year doy = time.datetime.timetuple().tm_yday secs = time.hour * 3600 + time.minute * 60 + time.second - usecs = time.microsecond - return (yr, doy, secs, usecs) + return (yr, doy, secs, rounded_usecs) def _open_socket(self): """Open a socket diff --git a/test/edge_test/RawInputClient_test.py b/test/edge_test/RawInputClient_test.py index 850f1a7df..5996fd79e 100644 --- a/test/edge_test/RawInputClient_test.py +++ b/test/edge_test/RawInputClient_test.py @@ -1,6 +1,8 @@ """Tests for RawInputClient.py""" import numpy +from datetime import datetime +import logging from obspy.core import Stats, Trace, UTCDateTime from geomagio.edge import EdgeFactory, RawInputClient from numpy.testing import assert_equal @@ -56,7 +58,7 @@ def test_raw_input_client(): def test__get_tag(): - """edge_test.RawInputClient_test.test_raw_input_client()""" + """edge_test.RawInputClient_test.test__get_tag()""" network = "NT" station = "BOU" channel = "MVH" @@ -72,3 +74,36 @@ def test__get_tag(): ) tag_send = client._get_tag() assert_equal(tag_send is not None, True) + + +def test__get_time_values(caplog): + """edge_test.RawInputClient_test.test__get_time_values()""" + network = "NT" + station = "BOU" + channel = "MVH" + location = "R0" + client = MockRawInputClient( + tag="tag", + host="host", + port="port", + station=station, + channel=channel, + 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 + assert_equal( + caplog.text, + "WARNING root:RawInputClient.py:434 residual microsecond values encountered, rounding to nearest microsecond\n", + ) + 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) -- GitLab From 99e182fb926ff04dfc5f28009c3f26f3919c1c17 Mon Sep 17 00:00:00 2001 From: pcain-usgs <pcain@usgs.gov> Date: Tue, 6 Oct 2020 16:41:19 -0600 Subject: [PATCH 2/5] get message from caplog --- test/edge_test/RawInputClient_test.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test/edge_test/RawInputClient_test.py b/test/edge_test/RawInputClient_test.py index 5996fd79e..73f2e1984 100644 --- a/test/edge_test/RawInputClient_test.py +++ b/test/edge_test/RawInputClient_test.py @@ -97,9 +97,10 @@ def test__get_time_values(caplog): 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( - caplog.text, - "WARNING root:RawInputClient.py:434 residual microsecond values encountered, rounding to nearest microsecond\n", + 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 -- GitLab 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 3/5] 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 From f7df44998934c94f43e93d1c2414d80e99414b02 Mon Sep 17 00:00:00 2001 From: Jeremy M Fee <jmfee@usgs.gov> Date: Wed, 7 Oct 2020 18:47:17 +0000 Subject: [PATCH 4/5] Update `usecs` value to match variable name. Add test for round-to-next-second branch. --- geomagio/TimeseriesUtility.py | 4 ++-- test/TimeseriesUtility_test.py | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/geomagio/TimeseriesUtility.py b/geomagio/TimeseriesUtility.py index 9aa99a895..adecbcb06 100644 --- a/geomagio/TimeseriesUtility.py +++ b/geomagio/TimeseriesUtility.py @@ -550,9 +550,9 @@ def round_usecs(time): time: UTCDateTime time containing rounded(or non-rounded) microsecond values """ - usecs = time.microsecond / 1000 + usecs = time.microsecond # round microseconds to nearest millisecond - rounded_usecs = int(round(usecs, 0)) * 1000 + rounded_usecs = int(round(usecs / 1000, 0)) * 1000 # reset microseconds to 0 at top of second, add second to input time if rounded_usecs > 999000: rounded_usecs = 0 diff --git a/test/TimeseriesUtility_test.py b/test/TimeseriesUtility_test.py index 512db7edf..a664b2e28 100644 --- a/test/TimeseriesUtility_test.py +++ b/test/TimeseriesUtility_test.py @@ -460,6 +460,9 @@ def test_round_usecs(): # 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")) + # test case with rounding to next second + time = TimeseriesUtility.round_usecs(UTCDateTime("2020-10-07T00:00:00.9995Z")) + assert_equal(time, UTCDateTime("2020-10-07T00:00:01.000Z")) def _create_trace(data, channel, starttime, delta=60.0): -- GitLab From a92374167549afc62952e6e8c7f928dce85bfc6f Mon Sep 17 00:00:00 2001 From: Jeremy M Fee <jmfee@usgs.gov> Date: Wed, 7 Oct 2020 19:33:53 +0000 Subject: [PATCH 5/5] Use UTCDateTime.replace method for rounded_usecs. instead of deprecated microsecond property setter. --- geomagio/TimeseriesUtility.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/geomagio/TimeseriesUtility.py b/geomagio/TimeseriesUtility.py index adecbcb06..418bbe9c1 100644 --- a/geomagio/TimeseriesUtility.py +++ b/geomagio/TimeseriesUtility.py @@ -557,5 +557,6 @@ def round_usecs(time): if rounded_usecs > 999000: rounded_usecs = 0 time += 1 - time.microsecond = rounded_usecs + if rounded_usecs != usecs: + time = time.replace(microsecond=rounded_usecs) return time -- GitLab