Repository: ulmo-dev/ulmo (language: python)

Issue:
Added NOAA GOES parser
I forgot to add the noaa files to my previous pull request. Sorry about that. This should replace eddn. 
support USGS rest services
this is currently a work in progress but at the moment it is awkwardly tied to the wateroneflow caching stuff which, it turns out, is kind of awful



Discussion / hints:

Hi Andy - I'm very interested in using your library to provide access to wateroneflow sites, but I have a requirement for accessing the USGS waterservices rest services.  I was curious if you have any ETA (even a guesstimate) on support for these services?
Chris

Hi Chris. 

The USGS rest services stuff is about 3/4ths-baked right now. It works in that I'm successfully using it to query/cache data for sites we're interested in but it's poorly documented and there are more REST parameters that have yet to be implemented (doing so wouldn't take much effort). As a guesstimate - I'm hoping to have things more or less stable in about a month.

The wateroneflow stuff is currently working but subject to overhaul. The API is weird and the code behind it is pretty ugly. Instead of doing magical things with objects and metaclasses the API should be more direct - where functions make WOF calls and return data and cache it to a database if need be. That's a pretty low priority, though, so it's probably a few months off.

For the near future the focus is on the USGS services. If you can stomach it, the WOF stuff works but it's not pretty.

If you do decide to use any of this your feedback would be very welcome. Pull requests, too.

Andy,

That's excellent news!  In terms of our current goals, the USGS portion of the library would be the most valuable (the wateroneflow is a "bonus", but not critical right now - so we can afford to wait until post-overhaul).

I need to verify with a couple people, but I believe we may be able to contribute to the USGS work.  At the very least, we will certainly be happy to provide feedback on usage/issues.

I'll let you know as soon as I get the go ahead.

Thanks,
Chris

so this is pretty stable now (and has been for a few months)

closing the general issue but feel free to open issues or let me know if there's anything the current implementation is lacking


Fix patch:
diff --git a/.gitignore b/.gitignore
index ecfbc008..9ae89be2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -17,4 +17,7 @@ docs/_build/
 .coverage
 
 # Ignore checkpoints created (especially for the examples)
-.ipynb_checkpoints
\ No newline at end of file
+.ipynb_checkpoints
+
+# ignore tox's python versions, logs, etc.
+.tox
diff --git a/ulmo/__init__.py b/ulmo/__init__.py
index 5c88806f..e42c75c4 100644
--- a/ulmo/__init__.py
+++ b/ulmo/__init__.py
@@ -15,6 +15,7 @@
 from . import lcra
 from . import nasa
 from . import ncdc
+from . import noaa
 from . import twc
 from . import usace
 from . import usgs
diff --git a/ulmo/noaa/__init__.py b/ulmo/noaa/__init__.py
new file mode 100644
index 00000000..2a893f42
--- /dev/null
+++ b/ulmo/noaa/__init__.py
@@ -0,0 +1,1 @@
+from . import goes
diff --git a/ulmo/noaa/goes/__init__.py b/ulmo/noaa/goes/__init__.py
new file mode 100644
index 00000000..a7c9d749
--- /dev/null
+++ b/ulmo/noaa/goes/__init__.py
@@ -0,0 +1,1 @@
+from .core import get_data, decode
diff --git a/ulmo/noaa/goes/core.py b/ulmo/noaa/goes/core.py
new file mode 100644
index 00000000..7f514dba
--- /dev/null
+++ b/ulmo/noaa/goes/core.py
@@ -0,0 +1,191 @@
+from datetime import datetime, timedelta
+import requests
+import pandas as pd
+from . import parsers
+import os
+from ulmo import util
+import isodate
+import shutil
+import logging
+
+
+dcs_url = 'https://dcs1.noaa.gov/Account/FieldTestData'
+
+DEFAULT_FILE_PATH = 'noaa/goes/'
+
+# configure logging
+LOG_FORMAT = '%(message)s'
+logging.basicConfig(format=LOG_FORMAT)
+log = logging.getLogger(__name__)
+log.setLevel(logging.INFO)
+
+
+def decode(dataframe, parser, **kwargs):
+    """decodes goes message data in pandas dataframe returned by
+    ulmo.noaa.goes.get_data().
+
+    Parameters
+    ----------
+    dataframe : pandas.DataFrame
+        pandas.DataFrame returned by ulmo.noaa.goes.get_data()
+    parser : {function, str}
+        function that acts on dcp_message each row of the dataframe and returns
+        a new dataframe containing several rows of decoded data. This returned
+        dataframe may have different (but derived) timestamps than that the
+        original row. If a string is passed then a matching parser function is
+        looked up from ulmo.noaa.goes.parsers
+
+    Returns
+    -------
+    decoded_data : pandas.DataFrame
+        pandas dataframe, the format and parameters in the returned dataframe
+        depend wholly on the parser used
+
+    """
+    if isinstance(parser, str):
+        parser = getattr(parsers, parser)
+
+    if dataframe.empty:
+        return dataframe
+
+    df = []
+    for timestamp, data in dataframe.iterrows():
+        parsed = parser(data, **kwargs)
+        parsed.dropna(how='all', inplace=True)
+        if parsed.empty:
+            empty_df = pd.DataFrame()
+            df.append(empty_df)
+        df.append(parsed)
+
+    df = pd.concat(df)
+    # preserve metadata in df if it exists, since pivot will lose it
+    df_save = df.drop(['channel', 'channel_data'], axis=1)
+    df = df.pivot_table(
+        index=df.index, columns='channel', values='channel_data'
+    ).join(df_save)
+
+    # to properly drop duplicate rows, need to include index; unfortunately,
+    df['idx'] = df.index.values
+    df = df.drop_duplicates().drop('idx', axis=1)
+    return df
+
+
+def get_data(dcp_address, hours, use_cache=False, cache_path=None,
+             as_dataframe=True):
+    """Fetches GOES Satellite DCP messages from NOAA Data Collection System
+    (DCS) field test.
+
+    Parameters
+    ----------
+    dcp_address : str, iterable of strings
+        DCP address or list of DCP addresses to be fetched; lists will be
+        joined by a ','.
+    use_cache : bool,
+        If True (default) use hdf file to cache data and retrieve new data on
+        subsequent requests
+    cache_path : {``None``, str},
+        If ``None`` use default ulmo location for cached files otherwise use
+        specified path. files are named using dcp_address.
+    as_dataframe : bool
+        If True (default) return data in a pandas dataframe otherwise return a
+        dict.
+
+    Returns
+    -------
+    message_data : {pandas.DataFrame, dict}
+        Either a pandas dataframe or a dict indexed by dcp message times
+    """
+
+    if isinstance(dcp_address, list):
+        dcp_address = ','.join(dcp_address)
+
+    data = pd.DataFrame()
+
+    if use_cache:
+        dcp_data_path = _get_store_path(cache_path, dcp_address + '.h5')
+        if os.path.exists(dcp_data_path):
+            data = pd.read_hdf(dcp_data_path, dcp_address)
+    params = {}
+    params['addr'] = dcp_address,
+    params['hours'] = hours,
+
+    messages = _fetch_url(params)
+    new_data = pd.DataFrame([_parse(row) for row in messages])
+
+    if not new_data.empty:
+        new_data.index = new_data.message_timestamp_utc
+        data = new_data.combine_first(data)
+        data.sort_index(inplace=True)
+        if use_cache:
+            # write to a tmp file and move to avoid ballooning h5 file
+            tmp = dcp_data_path + '.tmp'
+            data.to_hdf(tmp, dcp_address)
+            shutil.move(tmp, dcp_data_path)
+
+    if data.empty:
+        if as_dataframe:
+            return data
+        else:
+            return {}
+
+    if not as_dataframe:
+        data = data.T.to_dict()
+    return data
+
+
+def _fetch_url(params):
+    r = requests.post(dcs_url, params=params, timeout=60)
+    messages = r.json()
+    return messages
+
+
+def _format_period(period):
+    days, hours, minutes = period.days, period.seconds // 3600, \
+                           (period.seconds // 60) % 60
+
+    if minutes:
+        return 'now -%s minutes' % period.seconds / 60
+
+    if hours:
+        return 'now -%s hours' % period.seconds / 3600
+
+    if days:
+        return 'now -%s days' % days
+
+
+def _format_time(timestamp):
+
+    if isinstance(timestamp, str):
+        if timestamp.startswith('P'):
+            timestamp = isodate.parse_duration(timestamp)
+        else:
+            timestamp = isodate.parse_datetime(timestamp)
+
+    if isinstance(timestamp, datetime):
+        return timestamp.strftime('%Y/%j %H:%M:%S')
+    elif isinstance(timestamp, timedelta):
+        return _format_period(timestamp)
+
+
+def _get_store_path(path, default_file_name):
+    if path is None:
+        path = os.path.join(util.get_ulmo_dir(), DEFAULT_FILE_PATH)
+
+    if not os.path.exists(path):
+        os.makedirs(path)
+
+    return os.path.join(path, default_file_name)
+
+
+def _parse(entry):
+    return {
+        'dcp_address': entry['TblDcpDataAddrCorr'],
+        'message_timestamp_utc': datetime.fromtimestamp(
+            int(entry['TblDcpDataDtMsgCar'].strip('/Date()'))/1000
+        ),
+        'failure_code': entry['TblDcpDataProcessInfo'],
+        'signal_strength': entry['TblDcpDataSigStrength'],
+        'goes_receive_channel': entry['TblDcpDataChan'],
+        'message_data_length': entry['TblDcpDataDataLen'],
+        'dcp_message': entry['TblDcpDataData'],
+    }
diff --git a/ulmo/noaa/goes/parsers.py b/ulmo/noaa/goes/parsers.py
new file mode 100644
index 00000000..49ad2624
--- /dev/null
+++ b/ulmo/noaa/goes/parsers.py
@@ -0,0 +1,297 @@
+from datetime import timedelta
+import pandas as pd
+import numpy as np
+
+battery_names = ['bl', 'vb', 'bv']
+
+
+def twdb_dot(df_row, dual_well=False, drop_dcp_metadata=True):
+    """Parser for twdb DOT dataloggers."""
+    return _twdb_stevens_or_dot(df_row, reverse=False, dual_well=dual_well,
+                                drop_dcp_metadata=drop_dcp_metadata)
+
+
+def twdb_fts(df_row, drop_dcp_metadata=True, dual_well=False):
+    """Parser for twdb fts dataloggers
+
+    format examples:
+    C510D20018036133614G39-0NN170WXW00097  :WL 31 #60 -72.91 -72.89 -72.89 -72.89 -72.91 -72.92 -72.93 -72.96 -72.99 -72.97 -72.95 -72.95
+    """
+
+    message = df_row['dcp_message'].lower()
+    message_timestamp = df_row['message_timestamp_utc']
+    if _invalid_message_check(message):
+        return(_empty_df(message_timestamp))
+
+    data = []
+    for line in message.strip('"').split(':'):
+        try:
+            channel = line[:2]
+            channel_data = [
+                float(val.strip('+-')) for val in line[10:].split()
+            ]
+            df = _twdb_assemble_dataframe(
+                message_timestamp, channel, channel_data, reverse=False
+            )
+            data.append(df)
+        except Exception as e:
+            print('Warning: Could not parse values for channel {}: {}'.format(channel, e))
+
+    df = pd.concat(data)
+
+    if not drop_dcp_metadata:
+        for col in df_row.index:
+            df[col] = df_row[col]
+
+    return df
+
+
+def twdb_stevens(df_row, dual_well=False, drop_dcp_metadata=True):
+    """Parser for twdb stevens dataloggers."""
+    return _twdb_stevens_or_dot(df_row, reverse=True, dual_well=dual_well,
+                                drop_dcp_metadata=drop_dcp_metadata)
+
+
+def twdb_sutron(df_row, drop_dcp_metadata=True, dual_well=False):
+    """Parser for twdb sutron dataloggers.
+    Data is transmitted every 12 hours and each message contains 12 water level
+    measurements on the hour for the previous 12 hours and one battery voltage
+    measurement for the current hour
+
+    format examples:
+
+    '":ott 60 #60 -190.56 -190.66 -190.69 -190.71 -190.74 -190.73 -190.71 -190.71 -190.71 -190.71 -190.72 -190.72 :BL 13.05  '
+    '":SENSE01 60 #60 -82.19 -82.19 -82.18 -82.19 -82.19 -82.22 -82.24 -82.26 -82.27 -82.28 -82.28 -82.26 :BL 12.41  '
+    '":OTT 703 60 #60 -231.47 -231.45 -231.44 -231.45 -231.47 -231.50 -231.51 -231.55 -231.56 -231.57 -231.55 -231.53 :6910704 60 #60 -261.85 -261.83 -261.81 -261.80 -261.81 -261.83 -261.85 -261.87 -261.89 -261.88 -261.86 -261.83 :BL 13.21'
+    '":Sense01 10 #10 -44.70 -44.68 -44.66 -44.65 -44.63 -44.61 -44.60 -44.57 -44.56 -44.54 -44.52 -44.50 :BL 13.29'
+    '"\r\n-101.11 \r\n-101.10 \r\n-101.09 \r\n-101.09 \r\n-101.08 \r\n-101.08 \r\n-101.08 \r\n-101.10 \r\n-101.11 \r\n-101.09 \r\n-101.09 \r\n-101.08'
+    '"\r\n// \r\n// \r\n// \r\n// \r\n// \r\n-199.88 \r\n-199.92 \r\n-199.96 \r\n-199.98 \r\n-200.05 \r\n-200.09 \r\n-200.15'
+    '":Sense01 60 #60 M M M M M M M M M M M M :BL 12.65'
+    """
+    message = df_row['dcp_message'].strip().lower()
+    message_timestamp = df_row['message_timestamp_utc']
+    if _invalid_message_check(message):
+        return(_empty_df(message_timestamp))
+
+    lines = message.strip('":').split(':')
+    data = []
+    for line in lines:
+        lsplit = line.split(' ')
+        channel = lsplit[0]
+        if channel.lower() in battery_names:
+            try:
+                channel_data = [field.strip('+-" ') for field in lsplit]
+                df = _twdb_assemble_dataframe(
+                    message_timestamp, channel, channel_data, reverse=False
+                )
+                data.append(df)
+            except Exception as e:
+                print('Warning: Could not parse values for channel {}: {}'.format(channel, e))
+        else:
+            try:
+                channel_data = [field.strip('+-" ') for field in lsplit[3:]]
+                df = _twdb_assemble_dataframe(
+                    message_timestamp, channel, channel_data, reverse=False
+                )
+                data.append(df)
+            except Exception as e:
+                print('Warning: Could not parse values for channel {}: {}'.format(channel, e))
+
+    df = pd.concat(data)
+
+    if not drop_dcp_metadata:
+        for col in df_row.index:
+            df[col] = df_row[col]
+
+    return df
+
+
+def twdb_texuni(dataframe, drop_dcp_metadata=True, dual_well=False):
+    """Parser for twdb texuni dataloggers.
+    Data is transmitted every 12 hours and each message contains 12 water level
+    measurements on the hour for the previous 12 hours
+
+    format examples:
+    '"\r\n+0.000,-245.3,\r\n+0.000,-245.3,\r\n+0.000,-245.3,\r\n+0.000,-245.4,\r\n+0.000,-245.4,\r\n+0.000,-245.4,\r\n+0.000,-245.4,\r\n+0.000,-245.4,\r\n+0.000,-245.4,\r\n+0.000,-245.4,\r\n+0.000,-245.4,\r\n+0.000,-245.4,\r\n+0.000,-245.4,\r\n+0.000,-245.4,\r\n+0.000,-245.4,\r\n+0.000,-245.4,\r\n+0.000,-245.5,\r\n+0.000,-245.5,\r\n+0.000,-245.6,\r\n+0.000,-245.6,\r\n+0.000,-245.6,\r\n+0.000,-245.6,\r\n+0.000,-245.6,\r\n+0.000,-245.6,\r\n+412.0,+2013.,+307.0,+1300.,+12.75,+0.000,-245.4,-245.3,-245.6,+29.55,'
+    ' \r\n+0.000,-109.8,\r\n+0.000,-109.8,\r\n+0.000,-109.8,\r\n+0.000,-109.8,\r\n+0.000,-109.8,\r\n+0.000,-109.9,\r\n+0.000,-109.9,\r\n+0.000,-109.9,\r\n+0.000,-109.9,\r\n+0.000,-109.9,\r\n+0.000,-110.0,\r\n+0.000,-110.0,\r\n+0.000,-109.9,\r\n+0.000,-109.9,\r\n+0.000,-109.9,\r\n+0.000,-109.9,\r\n+0.000,-110.0,\r\n+0.000,-110.0,\r\n+0.000,-110.0,\r\n+0.000,-110.1,\r\n+0.000,-110.1,\r\n+0.000,-110.1,\r\n+0.000,-110.1,\r\n+0.000,-110.1,\r\n+340.0,+2013.,+307.0,+1400.,+12.07,+0.000,-109.9,-109.8,-110.1,+30.57,'
+    """
+
+    message = dataframe['dcp_message']
+    message_timestamp = dataframe['message_timestamp_utc']
+    channel = 'wl'
+    channel_data = [row.split(',')[1].strip('+- ') for
+                    row in message.strip('" \r\n').splitlines()[:-1]]
+
+    df = _twdb_assemble_dataframe(
+        message_timestamp, channel, channel_data, reverse=True
+    )
+
+    if not drop_dcp_metadata:
+        for col in dataframe.index:
+            df[col] = dataframe[col]
+
+    return df
+
+
+def _twdb_assemble_dataframe(message_timestamp, channel, channel_data,
+                             reverse=False):
+    data = []
+    base_timestamp = message_timestamp.replace(
+        minute=0, second=0, microsecond=0
+    )
+    if reverse:
+        data.reverse()
+
+    for hrs, value in enumerate(channel_data):
+        timestamp = base_timestamp - timedelta(hours=hrs)
+        try:
+            value = float(value)
+        except Exception:
+            value = np.nan
+
+        data.append([timestamp, channel, value])
+    if len(data) > 0:
+        df = pd.DataFrame(
+            data, columns=['timestamp_utc', 'channel', 'channel_data']
+        )
+        df.index = pd.to_datetime(df['timestamp_utc'])
+        del df['timestamp_utc']
+        return df
+    else:
+        return pd.DataFrame()
+
+
+def _twdb_stevens_or_dot(df_row, reverse, dual_well=False,
+                         drop_dcp_metadata=True):
+    """Parser for twdb stevens and DOT dataloggers - the only difference being
+    that with stevens dataloggers, water level data needs to be reversed to be
+    correctly interpretted.
+
+    Data is transmitted every 12 hours and each message contains 12 water level
+    measurements on the hour for the previous 12 hours and one battery voltage
+    measurement for the current hour
+
+    format examples:
+
+    '"BV:12.5  451.70$ 451.66$ 451.66$ 451.62$ 451.59$ 451.57$ 451.54$ 451.53$ 451.52$ 451.52$ 451.52$ 451.52$ '
+    '"BV:12.2  Channel:5 Time:43 +441.48 +443.25 +440.23 +440.67 +441.26 +441.85 +442.66 +443.84 +445.24 +442.15 +442.88 +443.91 '
+    '"BV:12.6  Channel:5 Time:28 +304.63 +304.63 +304.63 +304.56 +304.63 +304.63 +304.63 +304.63 +304.63 +304.63 +304.63 +304.71 Channel:6 Time:28 +310.51 +310.66 +310.59 +310.51 +310.51 +310.59 +310.59 +310.51 +310.66 +310.51 +310.66 +310.59 '
+    """
+    message = df_row['dcp_message'].strip().lower()
+    message_timestamp = df_row['message_timestamp_utc']
+    if _invalid_message_check(message):
+        return(_empty_df(message_timestamp))
+
+    data = []
+    if dual_well:
+        fields = message.strip('" \x10\x00').split('\r')
+        channel_data = {}
+        channel_data['bv'] = [fields[0].split(':')[1].split()[0]]
+        for field in fields[1:]:
+            df = pd.DataFrame()
+            try:
+                channel, channel_datum = field.strip('\n').split(': ')
+                if channel in channel_data:
+                    channel_data[channel].append(channel_datum.strip('+-'))
+                else:
+                    channel_data[channel] = [channel_datum.strip('+-')]
+            except Exception:
+                pass
+
+        for channel in channel_data:
+            df = _twdb_assemble_dataframe(
+                message_timestamp, channel, channel_data[channel],
+                reverse=reverse
+            )
+            data.append(df)
+
+        df = pd.concat(data)
+        if not drop_dcp_metadata:
+            for col in df_row.index:
+                df[col] = df_row[col]
+
+        return df
+
+    fields = message.strip('" ').split()
+
+    water_data = {}
+    channel_name = 'wl'
+    water_data[channel_name] = []
+    for field in fields:
+        df = pd.DataFrame()
+        field = field.lower().strip('\x10\x00')
+        if field[:2] in battery_names:
+            try:
+                channel, channel_data = field.split(':')
+                channel_data = [channel_data]
+                df = _twdb_assemble_dataframe(
+                    message_timestamp, channel, channel_data, reverse=reverse
+                )
+            except Exception as e:
+                print('Warning: Could not parse values for channel {}: {}'.format(field[:2], e))
+        elif 'time' in field:
+            try:
+                channel, channel_data = field.split(':')
+                channel_data = [channel_data]
+                df = _twdb_assemble_dataframe(
+                    message_timestamp, channel, channel_data, reverse=reverse
+                )
+            except Exception as e:
+                print('Warning: Could not parse values for channel {}: {}'.format(field.split(":")[0], e))
+        elif 'channel' in field:
+            try:
+                channel_name = field.split(':')[1]
+                if channel_name not in water_data:
+                    water_data[channel_name] = []
+            except Exception as e:
+                print('Warning: Could not parse values for channel {}: {}'.format(field.split(":")[1], e))
+        else:
+            try:
+                water_data[channel_name].append(float(field.strip('+-$')))
+            except Exception:
+                pass
+        data.append(df)
+
+    for channel in water_data:
+        data.append(
+            _twdb_assemble_dataframe(
+                message_timestamp, channel, water_data[channel],
+                reverse=reverse
+            )
+        )
+    df = pd.concat(data)
+
+    if not drop_dcp_metadata:
+        for col in df_row.index:
+            df[col] = df_row[col]
+
+    return df
+
+
+def _parse_value(water_level_str):
+    well_val = water_level_str.split(':')
+    if len(water_level_str.split(':')) == 2:
+        if well_val[1] == '':
+            val = np.nan
+        else:
+            val = well_val[1].strip('-')
+        value_dict = (well_val[0], val)
+        return value_dict
+    else:
+        return water_level_str
+
+
+def _invalid_message_check(message):
+    is_invalid = False
+    invalid_messages = ['dadds', 'operator', 'no']
+    for invalid in invalid_messages:
+        if invalid in message:
+            is_invalid = True
+    return(is_invalid)
+
+
+def _empty_df(message_timestamp):
+    df = _twdb_assemble_dataframe(
+        message_timestamp, np.nan, [np.nan]
+    )
+    return(df)
diff --git a/ulmo/usgs/__init__.py b/ulmo/usgs/__init__.py
index 1f421290..53f7fbf8 100644
--- a/ulmo/usgs/__init__.py
+++ b/ulmo/usgs/__init__.py
@@ -1,3 +1,2 @@
-from . import eddn
 from . import ned
 from . import nwis
diff --git a/ulmo/usgs/eddn/__init__.py b/ulmo/usgs/eddn/__init__.py
deleted file mode 100644
index 86e0d60f..00000000
--- a/ulmo/usgs/eddn/__init__.py
+++ /dev/null
@@ -1,8 +0,0 @@
-"""
-    `USGS Emergency Data Distribution Network`_ services
-
-
-    .. _USGS Emergency Data Distribution Network: http://eddn.usgs.gov/
-"""
-from .core import get_data, decode
-
diff --git a/ulmo/usgs/eddn/core.py b/ulmo/usgs/eddn/core.py
deleted file mode 100644
index d60ce298..00000000
--- a/ulmo/usgs/eddn/core.py
+++ /dev/null
@@ -1,299 +0,0 @@
-"""
-    ulmo.usgs.eddn.core
-    ~~~~~~~~~~~~~~~~~~~~~
-
-    This module provides access to data provided by the `United States Geological
-    Survey`_ `Emergency Data Distribution Network`_ web site.
-
-    The `DCP message format`_ includes some header information that is parsed and
-    the message body, with a variable number of characters. The format of the
-    message body varies widely depending on the manufacturer of the transmitter,
-    data logger, sensors, and the technician who programmed the DCP. The body can
-    be simple ASCII, sometime with parameter codes and time-stamps embedded,
-    sometimes not. The body can also be in 'Pseudo-Binary' which is character
-    encoding of binary data that uses 6 bits of every byte and guarantees that
-    all characters are printable.
-
-
-    .. _United States Geological Survey: http://www.usgs.gov/
-    .. _Emergency Data Distribution Network: http://eddn.usgs.gov/
-    .. _http://eddn.usgs.gov/dcpformat.html
-
-"""
-from past.builtins import basestring
-
-from bs4 import BeautifulSoup
-from datetime import datetime, timedelta
-import isodate
-import logging
-import os
-import pandas as pd
-import re
-import requests
-import shutil
-from ulmo import util
-
-from . import parsers
-
-# eddn query base url
-EDDN_URL = 'http://eddn.usgs.gov/cgi-bin/retrieveData.pl?%s'
-
-# default file path (appended to default ulmo path)
-DEFAULT_FILE_PATH = 'usgs/eddn/'
-
-# configure logging
-LOG_FORMAT = '%(message)s'
-logging.basicConfig(format=LOG_FORMAT)
-log = logging.getLogger(__name__)
-log.setLevel(logging.INFO)
-
-
-def decode(dataframe, parser, **kwargs):
-    """decodes dcp message data in pandas dataframe returned by ulmo.usgs.eddn.get_data().
-
-    Parameters
-    ----------
-    dataframe : pandas.DataFrame
-        pandas.DataFrame returned by ulmo.usgs.eddn.get_data()
-    parser : {function, str}
-        function that acts on dcp_message each row of the dataframe and returns a new dataframe
-        containing several rows of decoded data. This returned dataframe may have different
-        (but derived) timestamps than that the original row. If a string is passed then a matching
-        parser function is looked up from ulmo.usgs.eddn.parsers
-
-    Returns
-    -------
-    decoded_data : pandas.DataFrame
-        pandas dataframe, the format and parameters in the returned dataframe depend wholly on the parser used
-
-    """
-    if isinstance(parser, basestring):
-        parser = getattr(parsers, parser)
-
-    df = []
-    for timestamp, data in dataframe.iterrows():
-        parsed = parser(data, **kwargs)
-        parsed.dropna(how='all', inplace=True)
-        if not parsed.empty:
-            df.append(parsed)
-
-    df = pd.concat(df)
-    return df
-
-
-def get_data(
-        dcp_address, start=None, end=None, networklist='', channel='', spacecraft='Any', baud='Any',
-        electronic_mail='', dcp_bul='', glob_bul='', timing='', retransmitted='Y', daps_status='N',
-        use_cache=False, cache_path=None, as_dataframe=True):
-    """Fetches GOES Satellite DCP messages from USGS Emergency Data Distribution Network.
-
-    Parameters
-    ----------
-    dcp_address : str, iterable of strings
-        DCP address or list of DCP addresses to be fetched; lists will be joined by a ','.
-    start : {``None``, str, datetime, datetime.timedelta}
-        If ``None`` (default) then the start time is 2 days prior (or date of last data if cache is used)
-        If a datetime or datetime like string is specified it will be used as the start date.
-        If a timedelta or string in ISO 8601 period format (e.g 'P2D' for a period of 2 days) then
-        'now' minus the timedelta will be used as the start.
-        NOTE: The EDDN service does not specify how far back data is available. The service also imposes
-        a maximum data limit of 25000 character. If this is limit reached multiple requests will be made 
-        until all available data is downloaded. 
-    end : {``None``, str, datetime, datetime.timedelta}
-        If ``None`` (default) then the end time is 'now'
-        If a datetime or datetime like string is specified it will be used as the end date.
-        If a timedelta or string in ISO 8601 period format (e.g 'P2D' for a period of 2 days) then
-        'now' minus the timedelta will be used as the end.
-        NOTE: The EDDN service does not specify how far back data is available. The service also imposes
-        a maximum data limit of 25000 character.
-    networklist : str,
-        '' (default). Filter by network.
-    channel : str,
-        '' (default). Filter by channel.
-    spacecraft : str,
-        East, West, Any (default). Filter by GOES East/West Satellite
-    baud : str,
-        'Any' (default). Filter by baud rate. See http://eddn.usgs.gov/msgaccess.html for options
-    electronic_mail : str,
-        '' (default) or 'Y'
-    dcp_bul : str,
-        '' (default) or 'Y'
-    glob_bul : str,
-        '' (default) or 'Y'
-    timing : str,
-        '' (default) or 'Y'
-    retransmitted : str,
-        'Y' (default) or 'N'
-    daps_status : str,
-        'N' (default) or 'Y'
-    use_cache : bool,
-        If True (default) use hdf file to cache data and retrieve new data on subsequent requests
-    cache_path : {``None``, str},
-        If ``None`` use default ulmo location for cached files otherwise use specified path. files are named
-        using dcp_address.
-    as_dataframe : bool
-        If True (default) return data in a pandas dataframe otherwise return a dict.
-
-    Returns
-    -------
-    message_data : {pandas.DataFrame, dict}
-        Either a pandas dataframe or a dict indexed by dcp message times
-    """
-
-    if isinstance(dcp_address, list):
-        dcp_address = ','.join(dcp_address)
-
-    data = pd.DataFrame()
-
-    if use_cache:
-        dcp_data_path = _get_store_path(cache_path, dcp_address + '.h5')
-        if os.path.exists(dcp_data_path):
-            data = pd.read_hdf(dcp_data_path, dcp_address)
-
-    if start:
-        drs_since = _format_time(start)
-    else:
-        try:
-            drs_since = _format_time(data['message_timestamp_utc'][-1])
-        except:
-            drs_since = 'now -2 days'
-
-    if end:
-        drs_until = _format_time(end)
-    else:
-        drs_until = 'now'
-
-    params = {}
-    params['DCP_ADDRESS'] = dcp_address
-    params['DRS_SINCE'] = drs_since
-    params['DRS_UNTIL'] = drs_until
-    params['NETWORKLIST'] = networklist
-    params['CHANNEL'] = channel
-    params['BEFORE'] = '//START\n',
-    params['AFTER'] = '\n//END\n',
-    params['SPACECRAFT'] = spacecraft
-    params['BAUD'] = baud
-    params['ELECTRONIC_MAIL'] = electronic_mail
-    params['DCP_BUL'] = dcp_bul
-    params['GLOB_BUL'] = glob_bul
-    params['TIMING'] = timing
-    params['RETRANSMITTED'] = retransmitted
-    params['DAPS_STATUS'] = daps_status
-
-    data_limit_reached = True
-    messages = []
-    while data_limit_reached:
-        new_message, data_limit_reached =  _fetch_url(params)
-        messages += new_message
-        if data_limit_reached:
-            params['DRS_UNTIL'] = _format_time(_parse(new_message[-1])['message_timestamp_utc'])
-
-    new_data = pd.DataFrame([_parse(row) for row in messages])
-
-    if not new_data.empty:
-        new_data.index = new_data.message_timestamp_utc
-        data = new_data.combine_first(data)
-        data.sort_index(inplace=True)
-
-        if use_cache:
-            #write to a tmp file and move to avoid ballooning h5 file
-            tmp = dcp_data_path + '.tmp'
-            data.to_hdf(tmp, dcp_address)
-            shutil.move(tmp, dcp_data_path)
-
-    if data.empty:
-        if as_dataframe:
-            return data
-        else:
-            return {}
-
-    if start:
-        if start.startswith('P'):
-            start = data['message_timestamp_utc'][-1] - isodate.parse_duration(start)
-
-        data = data[start:]
-
-    if end:
-        if end.startswith('P'):
-            end = data['message_timestamp_utc'][-1] - isodate.parse_duration(end)
-
-        data = data[:end]
-
-    if not as_dataframe:
-        data = data.T.to_dict()
-
-    return data
-
-
-def _fetch_url(params):
-    r = requests.get(EDDN_URL, params=params)
-    log.info('data requested using url: %s\n' % r.url)
-    soup = BeautifulSoup(r.text)
-    message = soup.find('pre').contents[0].replace('\n', '').replace('\r', ' ')
-
-    data_limit_reached = False
-    if 'Max data limit reached' in message:
-        data_limit_reached = True
-        log.info('Max data limit reached, making new request for older data\n')
-
-    if not message:
-        log.info('No data found\n')
-        message = []
-    else:
-        message = [msg[1].strip() for msg in re.findall('(//START)(.*?)(//END)', message, re.M | re.S)]
-
-    return message, data_limit_reached
-
-
-def _format_period(period):
-    days, hours, minutes = period.days, period.seconds // 3600, (period.seconds // 60) % 60
-
-    if minutes:
-        return 'now -%s minutes' % period.seconds / 60
-
-    if hours:
-        return 'now -%s hours' % period.seconds / 3600
-
-    if days:
-        return 'now -%s days' % days
-
-
-def _format_time(timestamp):
-
-    if isinstance(timestamp, basestring):
-        if timestamp.startswith('P'):
-            timestamp = isodate.parse_duration(timestamp)
-        else:
-            timestamp = isodate.parse_datetime(timestamp)
-
-    if isinstance(timestamp, datetime):
-        return timestamp.strftime('%Y/%j %H:%M:%S')
-    elif isinstance(timestamp, timedelta):
-        return _format_period(timestamp)
-
-
-def _get_store_path(path, default_file_name):
-    if path is None:
-        path = os.path.join(util.get_ulmo_dir(), DEFAULT_FILE_PATH)
-
-    if not os.path.exists(path):
-        os.makedirs(path)
-
-    return os.path.join(path, default_file_name)
-
-
-def _parse(line):
-    return {
-        'dcp_address': line[:8],
-        'message_timestamp_utc': datetime.strptime(line[8:19], '%y%j%H%M%S'),
-        'failure_code': line[19:20],
-        'signal_strength': line[20:22],
-        'frequency_offset': line[22:24],
-        'modulation_index': line[24:25],
-        'data_quality_indicator': line[25:26],
-        'goes_receive_channel': line[26:29],
-        'goes_spacecraft_indicator': line[29:30],
-        'uplink_carrier_status': line[30:32],
-        'message_data_length': line[32:37],
-        'dcp_message': line[37:],
-    }
diff --git a/ulmo/usgs/eddn/parsers.py b/ulmo/usgs/eddn/parsers.py
deleted file mode 100644
index 82853445..00000000
--- a/ulmo/usgs/eddn/parsers.py
+++ /dev/null
@@ -1,216 +0,0 @@
-from datetime import timedelta
-import pandas as pd
-
-
-def twdb_dot(df_row, drop_dcp_metadata=True):
-    """Parser for twdb DOT dataloggers."""
-    return _twdb_stevens_or_dot(df_row, reverse=False, drop_dcp_metadata=drop_dcp_metadata)
-
-
-def twdb_fts(df_row, drop_dcp_metadata=True):
-    """Parser for twdb fts dataloggers
-
-    format examples:
-    C510D20018036133614G39-0NN170WXW00097  :WL 31 #60 -72.91 -72.89 -72.89 -72.89 -72.91 -72.92 -72.93 -72.96 -72.99 -72.97 -72.95 -72.95
-    """
-
-    message = df_row['dcp_message'].lower()
-    message_timestamp = df_row['message_timestamp_utc']
-
-    battery_voltage = pd.np.nan
-    for line in message.split(':'):
-        if line.split() != []:
-            line = line.split()
-            # grab water level data
-            if line[0] == 'wl':
-                water_levels = [
-                    field.strip('+- ') for field in line[3:]
-                ]
-            # grab battery voltage
-            if line[0] == 'vb':
-                battery_voltage = line[3].strip('+- ')
-
-    df = _twdb_assemble_dataframe(
-        message_timestamp, battery_voltage, water_levels, reverse=False
-    )
-    return df
-
-
-def twdb_stevens(df_row, drop_dcp_metadata=True):
-    """Parser for twdb stevens dataloggers."""
-    return _twdb_stevens_or_dot(df_row, reverse=True, drop_dcp_metadata=drop_dcp_metadata)
-
-
-def twdb_sutron(df_row, drop_dcp_metadata=True):
-    """Parser for twdb sutron dataloggers.
-    Data is transmitted every 12 hours and each message contains 12 water level measurements on the hour
-    for the previous 12 hours and one battery voltage measurement for the current hour
-
-    format examples:
-
-    '":ott 60 #60 -190.56 -190.66 -190.69 -190.71 -190.74 -190.73 -190.71 -190.71 -190.71 -190.71 -190.72 -190.72 :BL 13.05  '
-    '":SENSE01 60 #60 -82.19 -82.19 -82.18 -82.19 -82.19 -82.22 -82.24 -82.26 -82.27 -82.28 -82.28 -82.26 :BL 12.41  '
-    '":OTT 703 60 #60 -231.47 -231.45 -231.44 -231.45 -231.47 -231.50 -231.51 -231.55 -231.56 -231.57 -231.55 -231.53 :6910704 60 #60 -261.85 -261.83 -261.81 -261.80 -261.81 -261.83 -261.85 -261.87 -261.89 -261.88 -261.86 -261.83 :BL 13.21'
-    '":Sense01 10 #10 -44.70 -44.68 -44.66 -44.65 -44.63 -44.61 -44.60 -44.57 -44.56 -44.54 -44.52 -44.50 :BL 13.29'
-    '"\r\n-101.11 \r\n-101.10 \r\n-101.09 \r\n-101.09 \r\n-101.08 \r\n-101.08 \r\n-101.08 \r\n-101.10 \r\n-101.11 \r\n-101.09 \r\n-101.09 \r\n-101.08'
-    '"\r\n// \r\n// \r\n// \r\n// \r\n// \r\n-199.88 \r\n-199.92 \r\n-199.96 \r\n-199.98 \r\n-200.05 \r\n-200.09 \r\n-200.15'
-    '":Sense01 60 #60 M M M M M M M M M M M M :BL 12.65'
-    """
-    message = df_row['dcp_message'].lower()
-    message_timestamp = df_row['message_timestamp_utc']
-    lines = message.strip('":').split(':')
-    if len(lines) == 1:
-        water_levels = [field.strip('+- ') for field in lines[0].split()]
-        df = _twdb_assemble_dataframe(message_timestamp, None, water_levels, reverse=False)
-    else:
-        data = []
-        battery_voltage = lines[-1].split('bl')[-1].strip()
-        for line in lines[:-1]:
-            channel = line[:7]
-            split = line[7:].split()
-            water_levels = [field.strip('+-" ') for field in split[2:]]
-            df = _twdb_assemble_dataframe(message_timestamp, battery_voltage, water_levels, reverse=False)
-            df['channel'] = channel
-            data.append(df)
-        df = pd.concat(data)
-
-    if not drop_dcp_metadata:
-        for col in df_row.index:
-            df[col] = df_row[col]
-
-    return df
-
-
-def twdb_texuni(dataframe, drop_dcp_metadata=True):
-    """Parser for twdb texuni dataloggers.
-    Data is transmitted every 12 hours and each message contains 12 water level measurements on the hour
-    for the previous 12 hours
-
-    format examples:
-    '"\r\n+0.000,-245.3,\r\n+0.000,-245.3,\r\n+0.000,-245.3,\r\n+0.000,-245.4,\r\n+0.000,-245.4,\r\n+0.000,-245.4,\r\n+0.000,-245.4,\r\n+0.000,-245.4,\r\n+0.000,-245.4,\r\n+0.000,-245.4,\r\n+0.000,-245.4,\r\n+0.000,-245.4,\r\n+0.000,-245.4,\r\n+0.000,-245.4,\r\n+0.000,-245.4,\r\n+0.000,-245.4,\r\n+0.000,-245.5,\r\n+0.000,-245.5,\r\n+0.000,-245.6,\r\n+0.000,-245.6,\r\n+0.000,-245.6,\r\n+0.000,-245.6,\r\n+0.000,-245.6,\r\n+0.000,-245.6,\r\n+412.0,+2013.,+307.0,+1300.,+12.75,+0.000,-245.4,-245.3,-245.6,+29.55,'
-    ' \r\n+0.000,-109.8,\r\n+0.000,-109.8,\r\n+0.000,-109.8,\r\n+0.000,-109.8,\r\n+0.000,-109.8,\r\n+0.000,-109.9,\r\n+0.000,-109.9,\r\n+0.000,-109.9,\r\n+0.000,-109.9,\r\n+0.000,-109.9,\r\n+0.000,-110.0,\r\n+0.000,-110.0,\r\n+0.000,-109.9,\r\n+0.000,-109.9,\r\n+0.000,-109.9,\r\n+0.000,-109.9,\r\n+0.000,-110.0,\r\n+0.000,-110.0,\r\n+0.000,-110.0,\r\n+0.000,-110.1,\r\n+0.000,-110.1,\r\n+0.000,-110.1,\r\n+0.000,-110.1,\r\n+0.000,-110.1,\r\n+340.0,+2013.,+307.0,+1400.,+12.07,+0.000,-109.9,-109.8,-110.1,+30.57,'
-    """
-
-    message = dataframe['dcp_message']
-    message_timestamp = dataframe['message_timestamp_utc']
-    water_levels = [row.split(',')[1].strip('+- ') for row in message.strip('" \r\n').splitlines()[:-1]]
-
-    df = _twdb_assemble_dataframe(message_timestamp, None, water_levels, reverse=True)
-
-    if not drop_dcp_metadata:
-        for col in dataframe.index:
-            df[col] = dataframe[col]
-
-    return df
-
-
-def _twdb_assemble_dataframe(message_timestamp, battery_voltage, water_levels, reverse=False):
-    data = []
-    base_timestamp = message_timestamp.replace(minute=0, second=0, microsecond=0)
-    if reverse:
-        water_levels.reverse()
-    try:
-        battery_voltage = float(battery_voltage)
-    except:
-        battery_voltage = pd.np.nan
-
-    for hrs, water_level in enumerate(water_levels):
-        timestamp = base_timestamp - timedelta(hours=hrs)
-        try:
-            water_level = float(water_level)
-        except:
-            water_level = pd.np.nan
-
-        if hrs==0 and battery_voltage:
-            data.append([timestamp, battery_voltage, water_level])
-        else:
-            data.append([timestamp, pd.np.nan, water_level])
-
-    if len(data)>0:
-        df = pd.DataFrame(data, columns=['timestamp_utc', 'battery_voltage', 'water_level'])
-        df.index = pd.to_datetime(df['timestamp_utc'])
-        del df['timestamp_utc']
-        return df
-    else:
-        return pd.DataFrame()
-
-
-def _twdb_stevens_or_dot(df_row, reverse, drop_dcp_metadata=True):
-    """Parser for twdb stevens and DOT dataloggers - the only difference being
-    that with stevens dataloggers, water level data needs to be reversed to be
-    correctly interpretted.
-
-    Data is transmitted every 12 hours and each message contains 12 water level
-    measurements on the hour for the previous 12 hours and one battery voltage
-    measurement for the current hour
-
-    format examples:
-
-    '"BV:12.5  451.70$ 451.66$ 451.66$ 451.62$ 451.59$ 451.57$ 451.54$ 451.53$ 451.52$ 451.52$ 451.52$ 451.52$ '
-    '"BV:12.2  Channel:5 Time:43 +441.48 +443.25 +440.23 +440.67 +441.26 +441.85 +442.66 +443.84 +445.24 +442.15 +442.88 +443.91 '
-    '"BV:12.6  Channel:5 Time:28 +304.63 +304.63 +304.63 +304.56 +304.63 +304.63 +304.63 +304.63 +304.63 +304.63 +304.63 +304.71 Channel:6 Time:28 +310.51 +310.66 +310.59 +310.51 +310.51 +310.59 +310.59 +310.51 +310.66 +310.51 +310.66 +310.59 '
-    """
-    message = df_row['dcp_message'].strip().lower()
-    message_timestamp = df_row['message_timestamp_utc']
-    fields = message.strip().split()
-    if 'bv' in fields[0]:
-        battery_voltage = fields[0].split(':')[-1]
-        message = ' '.join(fields[1:])
-
-    else:
-        battery_voltage = None
-        message = ' '.join(fields)
-
-    fmt = '$+-"\x7f '
-
-    df = []
-    if 'channel' in message:
-        for channel_msg in message.strip('channel:').split('channel:'):
-            fields = channel_msg.split()
-            msg_channel = fields[0].split(':')[-1]
-            msg_time = fields[1].split(':')[-1]
-            water_levels = [field.strip(fmt) for field in fields[2:]]
-            data = _twdb_assemble_dataframe(message_timestamp, battery_voltage, water_levels, reverse=reverse)
-            data['channel'] = msg_channel
-            data['time'] = msg_time
-            df.append(data)
-    else:
-        fields = message.strip(fmt).lstrip().replace(': ', ':').split()
-        water_levels = [_parse_value(field.strip(fmt).lstrip()) for field in fields]
-        if len(water_levels) and isinstance(water_levels[0], tuple):
-            wells = list(set([val[0] for val in water_levels]))
-            combined = pd.DataFrame()
-            for well in wells:
-                values = [val[1] for val in water_levels
-                          if isinstance(val, tuple) and val[0] == well]
-                data = _twdb_assemble_dataframe(
-                    message_timestamp, battery_voltage, values, reverse=reverse)
-                data.rename(columns={'water_level': 'water_level_' + well},
-                            inplace=True)
-                combined = pd.concat([combined, data], axis=1).T.drop_duplicates().T
-            df.append(combined)
-        else:
-            data = _twdb_assemble_dataframe(message_timestamp, battery_voltage,
-                                            water_levels, reverse=reverse)
-            df.append(data)
-
-    df = pd.concat(df)
-
-    if not drop_dcp_metadata:
-        for col in df_row.index:
-            df[col] = df_row[col]
-
-    return df
-
-
-def _parse_value(water_level_str):
-    well_val = water_level_str.split(':')
-    if len(water_level_str.split(':')) == 2:
-        if well_val[1] == '':
-            val = pd.np.nan
-        else:
-            val = well_val[1].strip('-')
-        value_dict = (well_val[0], val)
-        return value_dict
-    else:
-        return water_level_str


Test patch:
diff --git a/test/files/noaa/goes/C5149430.txt b/test/files/noaa/goes/C5149430.txt
new file mode 100644
index 00000000..7d57c83f
--- /dev/null
+++ b/test/files/noaa/goes/C5149430.txt
@@ -0,0 +1,28 @@
+[
+  {
+    "TblDcpDataAddrCorr": "C5149430",
+    "TblDcpDataGroup": "TEXWDB",
+    "TblDcpDataChan": 166,
+    "TblDcpDataBaud": 300,
+    "TblDcpDataSigStrength": 48.4,
+    "TblDcpDataPhsNoise": 1.41,
+    "TblDcpDataGoodPhs": 100,
+    "TblDcpDataFreqDev1": 0.4,
+    "TblDcpDataDtMsgCar": "/Date(1559316497303)/",
+    "TblDcpDataDtMsgEnd": "/Date(1559316501353)/",
+    "TblDcpDataTimeMsg": 4.05,
+    "TblDcpDataProcessInfo": "G",
+    "TblDcpDataScid": 17,
+    "TblDcpDataFrameChar": "CS1",
+    "TblDcpDataDataLen": 123,
+    "TblDcpDataData": "\":Sense01 60 #60 -193.03 -191.62 -186.87 -186.87 -186.86 -186.87 -186.91 -186.94 -187.02 -187.07 -187.10 -187.13 :BL 12.81 ",
+    "TblDcpDataTimeMsgCar": 43616.64464472222,
+    "TblDcpDataTimeMsgEnd": 43616.64469159722,
+    "Id": 61981915,
+    "EntityIsActive": false,
+    "UpdateId": null,
+    "UpdateNo": 0,
+    "UpdateTime": null,
+    "DateCreated": "/Date(-62135596800000)/"
+  }
+]
diff --git a/test/files/noaa/goes/C514D73A.txt b/test/files/noaa/goes/C514D73A.txt
new file mode 100644
index 00000000..8f257f35
--- /dev/null
+++ b/test/files/noaa/goes/C514D73A.txt
@@ -0,0 +1,314 @@
+[
+  {
+    "TblDcpDataAddrCorr": "C514D73A",
+    "TblDcpDataGroup": "TEXWDB",
+    "TblDcpDataChan": 166,
+    "TblDcpDataBaud": 300,
+    "TblDcpDataSigStrength": 0,
+    "TblDcpDataPhsNoise": 0,
+    "TblDcpDataGoodPhs": 0,
+    "TblDcpDataFreqDev1": 0,
+    "TblDcpDataDtMsgCar": "/Date(1559158095000)/",
+    "TblDcpDataDtMsgEnd": "/Date(1559158105000)/",
+    "TblDcpDataTimeMsg": 10,
+    "TblDcpDataProcessInfo": "M",
+    "TblDcpDataScid": 0,
+    "TblDcpDataFrameChar": "N/A",
+    "TblDcpDataDataLen": 357,
+    "TblDcpDataData": "DADDS: PLATFORM [C514D73A]  OWNER [TEXWDB]  LOCATION [KERR5654405]  DECLARED [19/149 19:30:30]\r  PDT FIRST [07:28:15]  PERIOD [12:00:00]  WINDOW [00:10]\r  PDT [19/149 19:28:15.000] TO [19/149 19:28:25.000]  WIN [00:10.000]  PRI CHAN [166-S]\r  LST [19/148 07:28:15.299] TO [19/148 07:28:19.351]  LEN [00:04.052]  REC CHAN [166]\rMISSING SCHEDULED DCP MESSAGE\r",
+    "TblDcpDataTimeMsgCar": 43614.81128472222,
+    "TblDcpDataTimeMsgEnd": 43614.81140046296,
+    "Id": 60395658,
+    "EntityIsActive": false,
+    "UpdateId": null,
+    "UpdateNo": 0,
+    "UpdateTime": null,
+    "DateCreated": "/Date(-62135596800000)/"
+  },
+  {
+    "TblDcpDataAddrCorr": "C514D73A",
+    "TblDcpDataGroup": "TEXWDB",
+    "TblDcpDataChan": 166,
+    "TblDcpDataBaud": 300,
+    "TblDcpDataSigStrength": 0,
+    "TblDcpDataPhsNoise": 0,
+    "TblDcpDataGoodPhs": 0,
+    "TblDcpDataFreqDev1": 0,
+    "TblDcpDataDtMsgCar": "/Date(1559114895000)/",
+    "TblDcpDataDtMsgEnd": "/Date(1559114905000)/",
+    "TblDcpDataTimeMsg": 10,
+    "TblDcpDataProcessInfo": "M",
+    "TblDcpDataScid": 0,
+    "TblDcpDataFrameChar": "N/A",
+    "TblDcpDataDataLen": 357,
+    "TblDcpDataData": "DADDS: PLATFORM [C514D73A]  OWNER [TEXWDB]  LOCATION [KERR5654405]  DECLARED [19/149 07:30:30]\r  PDT FIRST [07:28:15]  PERIOD [12:00:00]  WINDOW [00:10]\r  PDT [19/149 07:28:15.000] TO [19/149 07:28:25.000]  WIN [00:10.000]  PRI CHAN [166-S]\r  LST [19/148 07:28:15.299] TO [19/148 07:28:19.351]  LEN [00:04.052]  REC CHAN [166]\rMISSING SCHEDULED DCP MESSAGE\r",
+    "TblDcpDataTimeMsgCar": 43614.31128472222,
+    "TblDcpDataTimeMsgEnd": 43614.31140046296,
+    "Id": 59962812,
+    "EntityIsActive": false,
+    "UpdateId": null,
+    "UpdateNo": 0,
+    "UpdateTime": null,
+    "DateCreated": "/Date(-62135596800000)/"
+  },
+  {
+    "TblDcpDataAddrCorr": "C514D73A",
+    "TblDcpDataGroup": "TEXWDB",
+    "TblDcpDataChan": 166,
+    "TblDcpDataBaud": 300,
+    "TblDcpDataSigStrength": 0,
+    "TblDcpDataPhsNoise": 0,
+    "TblDcpDataGoodPhs": 0,
+    "TblDcpDataFreqDev1": 0,
+    "TblDcpDataDtMsgCar": "/Date(1559071695000)/",
+    "TblDcpDataDtMsgEnd": "/Date(1559071705000)/",
+    "TblDcpDataTimeMsg": 10,
+    "TblDcpDataProcessInfo": "M",
+    "TblDcpDataScid": 0,
+    "TblDcpDataFrameChar": "N/A",
+    "TblDcpDataDataLen": 357,
+    "TblDcpDataData": "DADDS: PLATFORM [C514D73A]  OWNER [TEXWDB]  LOCATION [KERR5654405]  DECLARED [19/148 19:30:30]\r  PDT FIRST [07:28:15]  PERIOD [12:00:00]  WINDOW [00:10]\r  PDT [19/148 19:28:15.000] TO [19/148 19:28:25.000]  WIN [00:10.000]  PRI CHAN [166-S]\r  LST [19/148 07:28:15.299] TO [19/148 07:28:19.351]  LEN [00:04.052]  REC CHAN [166]\rMISSING SCHEDULED DCP MESSAGE\r",
+    "TblDcpDataTimeMsgCar": 43613.81128472222,
+    "TblDcpDataTimeMsgEnd": 43613.81140046296,
+    "Id": 59529675,
+    "EntityIsActive": false,
+    "UpdateId": null,
+    "UpdateNo": 0,
+    "UpdateTime": null,
+    "DateCreated": "/Date(-62135596800000)/"
+  },
+  {
+    "TblDcpDataAddrCorr": "C514D73A",
+    "TblDcpDataGroup": "TEXWDB",
+    "TblDcpDataChan": 166,
+    "TblDcpDataBaud": 300,
+    "TblDcpDataSigStrength": 41,
+    "TblDcpDataPhsNoise": 3.77,
+    "TblDcpDataGoodPhs": 94.5,
+    "TblDcpDataFreqDev1": 0.8,
+    "TblDcpDataDtMsgCar": "/Date(1559028495300)/",
+    "TblDcpDataDtMsgEnd": "/Date(1559028499350)/",
+    "TblDcpDataTimeMsg": 4.05199957,
+    "TblDcpDataProcessInfo": "G",
+    "TblDcpDataScid": 17,
+    "TblDcpDataFrameChar": "CS1",
+    "TblDcpDataDataLen": 123,
+    "TblDcpDataData": "\":Sense01 60 #60 -445.00 -445.00 -445.01 -445.02 -445.02 -445.02 -445.01 -444.99 -444.98 -444.96 -444.94 -444.93 :BL 12.97 ",
+    "TblDcpDataTimeMsgCar": 43613.31128818287,
+    "TblDcpDataTimeMsgEnd": 43613.311335081016,
+    "Id": 59096067,
+    "EntityIsActive": false,
+    "UpdateId": null,
+    "UpdateNo": 0,
+    "UpdateTime": null,
+    "DateCreated": "/Date(-62135596800000)/"
+  },
+  {
+    "TblDcpDataAddrCorr": "C514D73A",
+    "TblDcpDataGroup": "TEXWDB",
+    "TblDcpDataChan": 166,
+    "TblDcpDataBaud": 300,
+    "TblDcpDataSigStrength": 0,
+    "TblDcpDataPhsNoise": 0,
+    "TblDcpDataGoodPhs": 0,
+    "TblDcpDataFreqDev1": 0,
+    "TblDcpDataDtMsgCar": "/Date(1558985295000)/",
+    "TblDcpDataDtMsgEnd": "/Date(1558985305000)/",
+    "TblDcpDataTimeMsg": 10,
+    "TblDcpDataProcessInfo": "M",
+    "TblDcpDataScid": 0,
+    "TblDcpDataFrameChar": "N/A",
+    "TblDcpDataDataLen": 357,
+    "TblDcpDataData": "DADDS: PLATFORM [C514D73A]  OWNER [TEXWDB]  LOCATION [KERR5654405]  DECLARED [19/147 19:30:30]\r  PDT FIRST [07:28:15]  PERIOD [12:00:00]  WINDOW [00:10]\r  PDT [19/147 19:28:15.000] TO [19/147 19:28:25.000]  WIN [00:10.000]  PRI CHAN [166-S]\r  LST [19/147 07:28:15.320] TO [19/147 07:28:19.350]  LEN [00:04.030]  REC CHAN [166]\rMISSING SCHEDULED DCP MESSAGE\r",
+    "TblDcpDataTimeMsgCar": 43612.81128472222,
+    "TblDcpDataTimeMsgEnd": 43612.81140046296,
+    "Id": 58665647,
+    "EntityIsActive": false,
+    "UpdateId": null,
+    "UpdateNo": 0,
+    "UpdateTime": null,
+    "DateCreated": "/Date(-62135596800000)/"
+  },
+  {
+    "TblDcpDataAddrCorr": "C514D73A",
+    "TblDcpDataGroup": "TEXWDB",
+    "TblDcpDataChan": 166,
+    "TblDcpDataBaud": 300,
+    "TblDcpDataSigStrength": 41.6,
+    "TblDcpDataPhsNoise": 3.48,
+    "TblDcpDataGoodPhs": 97.3,
+    "TblDcpDataFreqDev1": 0.6,
+    "TblDcpDataDtMsgCar": "/Date(1558942095320)/",
+    "TblDcpDataDtMsgEnd": "/Date(1558942099350)/",
+    "TblDcpDataTimeMsg": 4.03,
+    "TblDcpDataProcessInfo": "G",
+    "TblDcpDataScid": 17,
+    "TblDcpDataFrameChar": "CS1",
+    "TblDcpDataDataLen": 123,
+    "TblDcpDataData": "\":Sense01 60 #60 -444.89 -444.90 -444.90 -444.90 -444.90 -444.90 -444.90 -444.90 -444.90 -444.89 -444.87 -444.86 :BL 12.97 ",
+    "TblDcpDataTimeMsgCar": 43612.311288425924,
+    "TblDcpDataTimeMsgEnd": 43612.31133506945,
+    "Id": 58233020,
+    "EntityIsActive": false,
+    "UpdateId": null,
+    "UpdateNo": 0,
+    "UpdateTime": null,
+    "DateCreated": "/Date(-62135596800000)/"
+  },
+  {
+    "TblDcpDataAddrCorr": "C514D73A",
+    "TblDcpDataGroup": "TEXWDB",
+    "TblDcpDataChan": 166,
+    "TblDcpDataBaud": 300,
+    "TblDcpDataSigStrength": 0,
+    "TblDcpDataPhsNoise": 0,
+    "TblDcpDataGoodPhs": 0,
+    "TblDcpDataFreqDev1": 0,
+    "TblDcpDataDtMsgCar": "/Date(1558898895000)/",
+    "TblDcpDataDtMsgEnd": "/Date(1558898905000)/",
+    "TblDcpDataTimeMsg": 10,
+    "TblDcpDataProcessInfo": "M",
+    "TblDcpDataScid": 0,
+    "TblDcpDataFrameChar": "N/A",
+    "TblDcpDataDataLen": 357,
+    "TblDcpDataData": "DADDS: PLATFORM [C514D73A]  OWNER [TEXWDB]  LOCATION [KERR5654405]  DECLARED [19/146 19:30:30]\r  PDT FIRST [07:28:15]  PERIOD [12:00:00]  WINDOW [00:10]\r  PDT [19/146 19:28:15.000] TO [19/146 19:28:25.000]  WIN [00:10.000]  PRI CHAN [166-S]\r  LST [19/146 07:28:15.295] TO [19/146 07:28:19.350]  LEN [00:04.055]  REC CHAN [166]\rMISSING SCHEDULED DCP MESSAGE\r",
+    "TblDcpDataTimeMsgCar": 43611.81128472222,
+    "TblDcpDataTimeMsgEnd": 43611.81140046296,
+    "Id": 57803028,
+    "EntityIsActive": false,
+    "UpdateId": null,
+    "UpdateNo": 0,
+    "UpdateTime": null,
+    "DateCreated": "/Date(-62135596800000)/"
+  },
+  {
+    "TblDcpDataAddrCorr": "C514D73A",
+    "TblDcpDataGroup": "TEXWDB",
+    "TblDcpDataChan": 166,
+    "TblDcpDataBaud": 300,
+    "TblDcpDataSigStrength": 41.6,
+    "TblDcpDataPhsNoise": 3.74,
+    "TblDcpDataGoodPhs": 95.9,
+    "TblDcpDataFreqDev1": 1.6,
+    "TblDcpDataDtMsgCar": "/Date(1558855695297)/",
+    "TblDcpDataDtMsgEnd": "/Date(1558855699350)/",
+    "TblDcpDataTimeMsg": 4.05500031,
+    "TblDcpDataProcessInfo": "G",
+    "TblDcpDataScid": 17,
+    "TblDcpDataFrameChar": "CS1",
+    "TblDcpDataDataLen": 123,
+    "TblDcpDataData": "\":Sense01 60 #60 -444.85 -444.85 -444.86 -444.88 -444.88 -444.89 -444.90 -444.90 -444.90 -444.89 -444.88 -444.86 :BL 12.89 ",
+    "TblDcpDataTimeMsgCar": 43611.311288136574,
+    "TblDcpDataTimeMsgEnd": 43611.31133506945,
+    "Id": 57370638,
+    "EntityIsActive": false,
+    "UpdateId": null,
+    "UpdateNo": 0,
+    "UpdateTime": null,
+    "DateCreated": "/Date(-62135596800000)/"
+  },
+  {
+    "TblDcpDataAddrCorr": "C514D73A",
+    "TblDcpDataGroup": "TEXWDB",
+    "TblDcpDataChan": 166,
+    "TblDcpDataBaud": 300,
+    "TblDcpDataSigStrength": 0,
+    "TblDcpDataPhsNoise": 0,
+    "TblDcpDataGoodPhs": 0,
+    "TblDcpDataFreqDev1": 0,
+    "TblDcpDataDtMsgCar": "/Date(1558812495000)/",
+    "TblDcpDataDtMsgEnd": "/Date(1558812505000)/",
+    "TblDcpDataTimeMsg": 10,
+    "TblDcpDataProcessInfo": "M",
+    "TblDcpDataScid": 0,
+    "TblDcpDataFrameChar": "N/A",
+    "TblDcpDataDataLen": 357,
+    "TblDcpDataData": "DADDS: PLATFORM [C514D73A]  OWNER [TEXWDB]  LOCATION [KERR5654405]  DECLARED [19/145 19:30:30]\r  PDT FIRST [07:28:15]  PERIOD [12:00:00]  WINDOW [00:10]\r  PDT [19/145 19:28:15.000] TO [19/145 19:28:25.000]  WIN [00:10.000]  PRI CHAN [166-S]\r  LST [19/145 07:28:15.303] TO [19/145 07:28:19.354]  LEN [00:04.051]  REC CHAN [166]\rMISSING SCHEDULED DCP MESSAGE\r",
+    "TblDcpDataTimeMsgCar": 43610.81128472222,
+    "TblDcpDataTimeMsgEnd": 43610.81140046296,
+    "Id": 56940246,
+    "EntityIsActive": false,
+    "UpdateId": null,
+    "UpdateNo": 0,
+    "UpdateTime": null,
+    "DateCreated": "/Date(-62135596800000)/"
+  },
+  {
+    "TblDcpDataAddrCorr": "C514D73A",
+    "TblDcpDataGroup": "TEXWDB",
+    "TblDcpDataChan": 166,
+    "TblDcpDataBaud": 300,
+    "TblDcpDataSigStrength": 41.1,
+    "TblDcpDataPhsNoise": 3.86,
+    "TblDcpDataGoodPhs": 95.7,
+    "TblDcpDataFreqDev1": 0.7,
+    "TblDcpDataDtMsgCar": "/Date(1558769295303)/",
+    "TblDcpDataDtMsgEnd": "/Date(1558769299353)/",
+    "TblDcpDataTimeMsg": 4.05099964,
+    "TblDcpDataProcessInfo": "G",
+    "TblDcpDataScid": 17,
+    "TblDcpDataFrameChar": "CS1",
+    "TblDcpDataDataLen": 123,
+    "TblDcpDataData": "\":Sense01 60 #60 -444.86 -444.85 -444.85 -444.87 -444.88 -444.89 -444.90 -444.92 -444.92 -444.90 -444.89 -444.88 :BL 12.89 ",
+    "TblDcpDataTimeMsgCar": 43610.31128822917,
+    "TblDcpDataTimeMsgEnd": 43610.31133511574,
+    "Id": 56506923,
+    "EntityIsActive": false,
+    "UpdateId": null,
+    "UpdateNo": 0,
+    "UpdateTime": null,
+    "DateCreated": "/Date(-62135596800000)/"
+  },
+  {
+    "TblDcpDataAddrCorr": "C514D73A",
+    "TblDcpDataGroup": "TEXWDB",
+    "TblDcpDataChan": 166,
+    "TblDcpDataBaud": 300,
+    "TblDcpDataSigStrength": 0,
+    "TblDcpDataPhsNoise": 0,
+    "TblDcpDataGoodPhs": 0,
+    "TblDcpDataFreqDev1": 0,
+    "TblDcpDataDtMsgCar": "/Date(1558726095000)/",
+    "TblDcpDataDtMsgEnd": "/Date(1558726105000)/",
+    "TblDcpDataTimeMsg": 10,
+    "TblDcpDataProcessInfo": "M",
+    "TblDcpDataScid": 0,
+    "TblDcpDataFrameChar": "N/A",
+    "TblDcpDataDataLen": 357,
+    "TblDcpDataData": "DADDS: PLATFORM [C514D73A]  OWNER [TEXWDB]  LOCATION [KERR5654405]  DECLARED [19/144 19:30:30]\r  PDT FIRST [07:28:15]  PERIOD [12:00:00]  WINDOW [00:10]\r  PDT [19/144 19:28:15.000] TO [19/144 19:28:25.000]  WIN [00:10.000]  PRI CHAN [166-S]\r  LST [19/144 07:28:15.314] TO [19/144 07:28:19.355]  LEN [00:04.041]  REC CHAN [166]\rMISSING SCHEDULED DCP MESSAGE\r",
+    "TblDcpDataTimeMsgCar": 43609.81128472222,
+    "TblDcpDataTimeMsgEnd": 43609.81140046296,
+    "Id": 56075131,
+    "EntityIsActive": false,
+    "UpdateId": null,
+    "UpdateNo": 0,
+    "UpdateTime": null,
+    "DateCreated": "/Date(-62135596800000)/"
+  },
+  {
+    "TblDcpDataAddrCorr": "C514D73A",
+    "TblDcpDataGroup": "TEXWDB",
+    "TblDcpDataChan": 166,
+    "TblDcpDataBaud": 300,
+    "TblDcpDataSigStrength": 40.7,
+    "TblDcpDataPhsNoise": 3.7,
+    "TblDcpDataGoodPhs": 95.7,
+    "TblDcpDataFreqDev1": 2,
+    "TblDcpDataDtMsgCar": "/Date(1558682895313)/",
+    "TblDcpDataDtMsgEnd": "/Date(1558682899357)/",
+    "TblDcpDataTimeMsg": 4.041,
+    "TblDcpDataProcessInfo": "G",
+    "TblDcpDataScid": 17,
+    "TblDcpDataFrameChar": "CS1",
+    "TblDcpDataDataLen": 123,
+    "TblDcpDataData": "\":Sense01 60 #60 -444.86 -444.85 -444.85 -444.86 -444.87 -444.89 -444.90 -444.91 -444.91 -444.91 -444.90 -444.89 :BL 12.65 ",
+    "TblDcpDataTimeMsgCar": 43609.31128835648,
+    "TblDcpDataTimeMsgEnd": 43609.31133512731,
+    "Id": 55642957,
+    "EntityIsActive": false,
+    "UpdateId": null,
+    "UpdateNo": 0,
+    "UpdateTime": null,
+    "DateCreated": "/Date(-62135596800000)/"
+  }
+]
diff --git a/test/files/noaa/goes/C516C1B8.txt b/test/files/noaa/goes/C516C1B8.txt
new file mode 100644
index 00000000..fdb0f35d
--- /dev/null
+++ b/test/files/noaa/goes/C516C1B8.txt
@@ -0,0 +1,236 @@
+[
+  {
+    "TblDcpDataAddrCorr": "C516C1B8",
+    "TblDcpDataGroup": "TEXWDB",
+    "TblDcpDataChan": 138,
+    "TblDcpDataBaud": 300,
+    "TblDcpDataSigStrength": 49.5,
+    "TblDcpDataPhsNoise": 1.52,
+    "TblDcpDataGoodPhs": 100,
+    "TblDcpDataFreqDev1": -64.5,
+    "TblDcpDataDtMsgCar": "/Date(1559569431753)/",
+    "TblDcpDataDtMsgEnd": "/Date(1559569438813)/",
+    "TblDcpDataTimeMsg": 7.058,
+    "TblDcpDataProcessInfo": "G",
+    "TblDcpDataScid": 17,
+    "TblDcpDataFrameChar": "CS1",
+    "TblDcpDataDataLen": 237,
+    "TblDcpDataData": "\"BV:12.7  Channel:5 Time:43 +298.76 +298.68 +298.83 +298.90 +298.76 +298.76 +298.68 +298.68 +298.83 +298.61 +298.68 +298.46 Channel:6 Time:43 +316.10 +316.24 +316.32 +316.17 +316.32 +316.17 +316.24 +316.10 +316.02 +316.02 +315.95 +315.87",
+    "TblDcpDataTimeMsgCar": 43619.572126782405,
+    "TblDcpDataTimeMsgEnd": 43619.57220847222,
+    "Id": 64504946,
+    "EntityIsActive": false,
+    "UpdateId": null,
+    "UpdateNo": 0,
+    "UpdateTime": null,
+    "DateCreated": "/Date(-62135596800000)/"
+  },
+  {
+    "TblDcpDataAddrCorr": "C516C1B8",
+    "TblDcpDataGroup": "TEXWDB",
+    "TblDcpDataChan": 138,
+    "TblDcpDataBaud": 300,
+    "TblDcpDataSigStrength": 49.5,
+    "TblDcpDataPhsNoise": 1.46,
+    "TblDcpDataGoodPhs": 100,
+    "TblDcpDataFreqDev1": -58.1,
+    "TblDcpDataDtMsgCar": "/Date(1559526232057)/",
+    "TblDcpDataDtMsgEnd": "/Date(1559526239123)/",
+    "TblDcpDataTimeMsg": 7.065,
+    "TblDcpDataProcessInfo": "G",
+    "TblDcpDataScid": 17,
+    "TblDcpDataFrameChar": "CS1",
+    "TblDcpDataDataLen": 237,
+    "TblDcpDataData": "\"BV:13.2  Channel:5 Time:43 +298.61 +298.39 +298.46 +298.31 +298.24 +298.24 +298.31 +298.39 +298.46 +298.46 +298.61 +298.68 Channel:6 Time:43 +315.80 +315.80 +315.80 +315.65 +315.65 +315.73 +315.73 +315.80 +315.87 +315.95 +315.95 +316.02",
+    "TblDcpDataTimeMsgCar": 43619.07213028935,
+    "TblDcpDataTimeMsgEnd": 43619.072212060186,
+    "Id": 64075481,
+    "EntityIsActive": false,
+    "UpdateId": null,
+    "UpdateNo": 0,
+    "UpdateTime": null,
+    "DateCreated": "/Date(-62135596800000)/"
+  },
+  {
+    "TblDcpDataAddrCorr": "C516C1B8",
+    "TblDcpDataGroup": "TEXWDB",
+    "TblDcpDataChan": 138,
+    "TblDcpDataBaud": 300,
+    "TblDcpDataSigStrength": 49.6,
+    "TblDcpDataPhsNoise": 1.59,
+    "TblDcpDataGoodPhs": 100,
+    "TblDcpDataFreqDev1": -65.7,
+    "TblDcpDataDtMsgCar": "/Date(1559483031733)/",
+    "TblDcpDataDtMsgEnd": "/Date(1559483038803)/",
+    "TblDcpDataTimeMsg": 7.07,
+    "TblDcpDataProcessInfo": "G",
+    "TblDcpDataScid": 17,
+    "TblDcpDataFrameChar": "CS1",
+    "TblDcpDataDataLen": 237,
+    "TblDcpDataData": "\"BV:12.7  Channel:5 Time:43 +298.76 +298.68 +298.83 +298.83 +298.76 +298.83 +298.76 +298.68 +298.76 +298.61 +298.68 +298.53 Channel:6 Time:43 +316.17 +316.02 +316.17 +316.10 +316.17 +316.10 +316.17 +316.02 +315.95 +316.02 +316.02 +315.87",
+    "TblDcpDataTimeMsgCar": 43618.572126527775,
+    "TblDcpDataTimeMsgEnd": 43618.57220835648,
+    "Id": 63644462,
+    "EntityIsActive": false,
+    "UpdateId": null,
+    "UpdateNo": 0,
+    "UpdateTime": null,
+    "DateCreated": "/Date(-62135596800000)/"
+  },
+  {
+    "TblDcpDataAddrCorr": "C516C1B8",
+    "TblDcpDataGroup": "TEXWDB",
+    "TblDcpDataChan": 138,
+    "TblDcpDataBaud": 300,
+    "TblDcpDataSigStrength": 49.7,
+    "TblDcpDataPhsNoise": 1.55,
+    "TblDcpDataGoodPhs": 100,
+    "TblDcpDataFreqDev1": -58.9,
+    "TblDcpDataDtMsgCar": "/Date(1559439832077)/",
+    "TblDcpDataDtMsgEnd": "/Date(1559439839147)/",
+    "TblDcpDataTimeMsg": 7.06900024,
+    "TblDcpDataProcessInfo": "G",
+    "TblDcpDataScid": 17,
+    "TblDcpDataFrameChar": "CS1",
+    "TblDcpDataDataLen": 237,
+    "TblDcpDataData": "\"BV:13.3  Channel:5 Time:43 +298.61 +298.46 +298.31 +298.31 +298.24 +298.17 +298.17 +298.31 +298.31 +298.46 +298.53 +298.68 Channel:6 Time:43 +315.80 +315.65 +315.58 +315.65 +315.73 +315.73 +315.58 +315.73 +315.73 +315.95 +315.95 +315.95",
+    "TblDcpDataTimeMsgCar": 43618.072130532404,
+    "TblDcpDataTimeMsgEnd": 43618.072212349536,
+    "Id": 63214508,
+    "EntityIsActive": false,
+    "UpdateId": null,
+    "UpdateNo": 0,
+    "UpdateTime": null,
+    "DateCreated": "/Date(-62135596800000)/"
+  },
+  {
+    "TblDcpDataAddrCorr": "C516C1B8",
+    "TblDcpDataGroup": "TEXWDB",
+    "TblDcpDataChan": 138,
+    "TblDcpDataBaud": 300,
+    "TblDcpDataSigStrength": 49.3,
+    "TblDcpDataPhsNoise": 1.49,
+    "TblDcpDataGoodPhs": 100,
+    "TblDcpDataFreqDev1": -64,
+    "TblDcpDataDtMsgCar": "/Date(1559396631737)/",
+    "TblDcpDataDtMsgEnd": "/Date(1559396638787)/",
+    "TblDcpDataTimeMsg": 7.053,
+    "TblDcpDataProcessInfo": "G",
+    "TblDcpDataScid": 17,
+    "TblDcpDataFrameChar": "CS1",
+    "TblDcpDataDataLen": 237,
+    "TblDcpDataData": "\"BV:12.7  Channel:5 Time:43 +298.76 +298.76 +298.68 +298.68 +298.76 +298.83 +298.68 +298.68 +298.68 +298.68 +298.68 +298.61 Channel:6 Time:43 +316.02 +316.17 +316.10 +316.17 +316.24 +316.10 +316.24 +316.10 +316.02 +316.10 +315.87 +315.95",
+    "TblDcpDataTimeMsgCar": 43617.5721265625,
+    "TblDcpDataTimeMsgEnd": 43617.57220819445,
+    "Id": 62783243,
+    "EntityIsActive": false,
+    "UpdateId": null,
+    "UpdateNo": 0,
+    "UpdateTime": null,
+    "DateCreated": "/Date(-62135596800000)/"
+  },
+  {
+    "TblDcpDataAddrCorr": "C516C1B8",
+    "TblDcpDataGroup": "TEXWDB",
+    "TblDcpDataChan": 138,
+    "TblDcpDataBaud": 300,
+    "TblDcpDataSigStrength": 49.6,
+    "TblDcpDataPhsNoise": 1.51,
+    "TblDcpDataGoodPhs": 100,
+    "TblDcpDataFreqDev1": -60.5,
+    "TblDcpDataDtMsgCar": "/Date(1559353431893)/",
+    "TblDcpDataDtMsgEnd": "/Date(1559353438970)/",
+    "TblDcpDataTimeMsg": 7.078,
+    "TblDcpDataProcessInfo": "G",
+    "TblDcpDataScid": 17,
+    "TblDcpDataFrameChar": "CS1",
+    "TblDcpDataDataLen": 237,
+    "TblDcpDataData": "\"BV:12.8  Channel:5 Time:43 +298.46 +298.31 +298.24 +298.24 +298.46 +298.53 +298.46 +298.39 +298.39 +298.53 +298.46 +298.61 Channel:6 Time:43 +315.73 +315.65 +315.65 +315.65 +315.80 +315.95 +315.80 +315.87 +315.95 +316.17 +316.02 +316.02",
+    "TblDcpDataTimeMsgCar": 43617.072128391206,
+    "TblDcpDataTimeMsgEnd": 43617.0722103125,
+    "Id": 62352330,
+    "EntityIsActive": false,
+    "UpdateId": null,
+    "UpdateNo": 0,
+    "UpdateTime": null,
+    "DateCreated": "/Date(-62135596800000)/"
+  },
+  {
+    "TblDcpDataAddrCorr": "C516C1B8",
+    "TblDcpDataGroup": "TEXWDB",
+    "TblDcpDataChan": 138,
+    "TblDcpDataBaud": 300,
+    "TblDcpDataSigStrength": 49.7,
+    "TblDcpDataPhsNoise": 1.52,
+    "TblDcpDataGoodPhs": 100,
+    "TblDcpDataFreqDev1": -65.9,
+    "TblDcpDataDtMsgCar": "/Date(1559310231723)/",
+    "TblDcpDataDtMsgEnd": "/Date(1559310238780)/",
+    "TblDcpDataTimeMsg": 7.057,
+    "TblDcpDataProcessInfo": "G",
+    "TblDcpDataScid": 17,
+    "TblDcpDataFrameChar": "CS1",
+    "TblDcpDataDataLen": 237,
+    "TblDcpDataData": "\"BV:12.6  Channel:5 Time:43 +298.68 +298.68 +298.76 +298.76 +298.76 +298.76 +298.83 +298.76 +298.76 +298.68 +298.68 +298.68 Channel:6 Time:43 +316.10 +316.10 +316.17 +316.10 +316.10 +316.24 +316.17 +316.02 +316.02 +316.10 +316.02 +315.95",
+    "TblDcpDataTimeMsgCar": 43616.57212642361,
+    "TblDcpDataTimeMsgEnd": 43616.572208101854,
+    "Id": 61919169,
+    "EntityIsActive": false,
+    "UpdateId": null,
+    "UpdateNo": 0,
+    "UpdateTime": null,
+    "DateCreated": "/Date(-62135596800000)/"
+  },
+  {
+    "TblDcpDataAddrCorr": "C516C1B8",
+    "TblDcpDataGroup": "TEXWDB",
+    "TblDcpDataChan": 138,
+    "TblDcpDataBaud": 300,
+    "TblDcpDataSigStrength": 49.7,
+    "TblDcpDataPhsNoise": 1.51,
+    "TblDcpDataGoodPhs": 100,
+    "TblDcpDataFreqDev1": -63.5,
+    "TblDcpDataDtMsgCar": "/Date(1559267031890)/",
+    "TblDcpDataDtMsgEnd": "/Date(1559267038937)/",
+    "TblDcpDataTimeMsg": 7.049,
+    "TblDcpDataProcessInfo": "G",
+    "TblDcpDataScid": 17,
+    "TblDcpDataFrameChar": "CS1",
+    "TblDcpDataDataLen": 237,
+    "TblDcpDataData": "\"BV:12.9  Channel:5 Time:43 +298.68 +298.61 +298.39 +298.31 +298.24 +298.39 +298.39 +298.39 +298.61 +298.68 +298.68 +298.61 Channel:6 Time:43 +315.87 +315.87 +315.80 +315.65 +315.73 +315.73 +315.73 +315.87 +316.02 +315.95 +316.02 +316.10",
+    "TblDcpDataTimeMsgCar": 43616.07212834491,
+    "TblDcpDataTimeMsgEnd": 43616.07220993056,
+    "Id": 61487268,
+    "EntityIsActive": false,
+    "UpdateId": null,
+    "UpdateNo": 0,
+    "UpdateTime": null,
+    "DateCreated": "/Date(-62135596800000)/"
+  },
+  {
+    "TblDcpDataAddrCorr": "C516C1B8",
+    "TblDcpDataGroup": "TEXWDB",
+    "TblDcpDataChan": 138,
+    "TblDcpDataBaud": 300,
+    "TblDcpDataSigStrength": 49.5,
+    "TblDcpDataPhsNoise": 1.43,
+    "TblDcpDataGoodPhs": 100,
+    "TblDcpDataFreqDev1": -66.3,
+    "TblDcpDataDtMsgCar": "/Date(1559223831713)/",
+    "TblDcpDataDtMsgEnd": "/Date(1559223838780)/",
+    "TblDcpDataTimeMsg": 7.06599951,
+    "TblDcpDataProcessInfo": "G",
+    "TblDcpDataScid": 17,
+    "TblDcpDataFrameChar": "CS1",
+    "TblDcpDataDataLen": 237,
+    "TblDcpDataData": "\"BV:12.6  Channel:5 Time:43 +298.76 +298.76 +298.83 +298.90 +298.83 +298.83 +298.83 +298.98 +298.83 +298.76 +298.83 +298.83 Channel:6 Time:43 +316.17 +316.10 +316.17 +316.10 +316.17 +316.10 +316.10 +316.10 +316.02 +316.10 +316.02 +316.02",
+    "TblDcpDataTimeMsgCar": 43615.572126307874,
+    "TblDcpDataTimeMsgEnd": 43615.57220809028,
+    "Id": 61053871,
+    "EntityIsActive": false,
+    "UpdateId": null,
+    "UpdateNo": 0,
+    "UpdateTime": null,
+    "DateCreated": "/Date(-62135596800000)/"
+  }
+]
diff --git a/test/files/usgs/eddn/C5149430.txt b/test/files/usgs/eddn/C5149430.txt
deleted file mode 100644
index 23fc870e..00000000
--- a/test/files/usgs/eddn/C5149430.txt
+++ /dev/null
@@ -1,14 +0,0 @@
-<PRE>
-//START
-C514943013305152818G46+0NN166WXW00106"BV:11.9  192.71$ 192.71$ 192.70$ 192.66$ 192.63$ 192.58$ 192.54$ 198.72$ 199.66$ 200.15$ 194.72$ 193.76$ 
-//END
-//START
-C514943013305032818G46+0HN166WXW00106"BV:12.9  192.91$ 192.89$ 192.86$ 192.80$ 192.76$ 192.72$ 192.71$ 192.70$ 192.70$ 192.70$ 192.71$ 192.71$ 
-//END
-//START
-C514943013304152818G46+0NN166WXW00106"BV:11.9  193.16$ 193.13$ 193.10$ 193.07$ 193.04$ 193.00$ 192.99$ 192.95$ 192.92$ 192.92$ 192.92$ 192.92$ 
-//END
-//START
-C514943013304032818G46+0HN166WXW00106"BV:12.5  194.03$ 193.81$ 193.66$ 193.53$ 193.41$ 193.32$ 193.26$ 193.22$ 193.20$ 193.19$ 193.17$ 193.16$ 
-//END
-</PRE>
diff --git a/test/files/usgs/eddn/C5149430_file1.txt b/test/files/usgs/eddn/C5149430_file1.txt
deleted file mode 100644
index 9c2c40dd..00000000
Binary files a/test/files/usgs/eddn/C5149430_file1.txt and /dev/null differ
diff --git a/test/files/usgs/eddn/C5149430_file2.txt b/test/files/usgs/eddn/C5149430_file2.txt
deleted file mode 100644
index ddaffc2a..00000000
--- a/test/files/usgs/eddn/C5149430_file2.txt
+++ /dev/null
@@ -1,528 +0,0 @@
-<PRE>
-//START
-C514943013294032818G46+0HN166WXW00106"BV:12.7  195.42$ 200.47$ 202.08$ 202.69$ 199.35$ 196.76$ 196.19$ 195.89$ 195.70$ 195.57$ 195.48$ 195.42$ 
-//END
-//START
-C514943013293152818G46+0NN166WXW00106"BV:11.8  196.37$ 196.20$ 196.06$ 195.96$ 195.87$ 195.77$ 195.69$ 195.62$ 195.56$ 195.51$ 195.47$ 195.45$ 
-//END
-//START
-C514943013293032818G45+0HN166WXW00106"BV:12.6  195.98$ 195.96$ 195.93$ 195.89$ 195.84$ 195.80$ 195.75$ 201.69$ 202.71$ 203.22$ 197.27$ 196.65$ 
-//END
-//START
-C514943013292152818G46+0HN166WXW00106"BV:11.8  196.31$ 196.29$ 196.26$ 196.23$ 196.18$ 196.14$ 196.10$ 196.07$ 196.04$ 196.02$ 196.01$ 195.99$ 
-//END
-//START
-C514943013292032818G45+0NN166WXW00106"BV:12.8  202.49$ 203.40$ 203.85$ 198.01$ 197.27$ 196.93$ 196.71$ 196.56$ 196.47$ 196.41$ 196.38$ 196.34$ 
-//END
-//START
-C514943013291152818G46+0HN166WXW00106"BV:11.7  199.37$ 198.02$ 197.54$ 197.27$ 197.07$ 196.92$ 196.77$ 196.68$ 196.61$ 196.55$ 196.50$ 196.47$ 
-//END
-//START
-C514943013291032818G45+0HN166WXW00106"BV:12.7  197.28$ 197.20$ 197.10$ 202.62$ 198.05$ 197.38$ 197.12$ 196.97$ 196.87$ 202.57$ 203.68$ 204.20$ 
-//END
-//START
-C514943013290152818G46+0NN166WXW00106"BV:11.7  197.27$ 197.22$ 197.18$ 197.12$ 197.06$ 203.25$ 204.07$ 199.80$ 198.18$ 197.73$ 197.52$ 197.40$ 
-//END
-//START
-C514943013290032818G46+0NN166WXW00106"BV:11.7  202.98$ 204.15$ 204.67$ 198.99$ 198.21$ 197.87$ 197.67$ 197.53$ 197.45$ 197.39$ 197.34$ 197.30$ 
-//END
-//START
-C514943013289152818G46+0NN166WXW00106"BV:11.8  197.46$ 197.44$ 197.39$ 197.35$ 197.32$ 197.28$ 197.25$ 197.23$ 197.23$ 197.22$ 197.22$ 197.23$ 
-//END
-//START
-C514943013289032818G46+0NN166WXW00106"BV:12.4  197.88$ 197.80$ 197.74$ 197.67$ 197.60$ 197.55$ 197.52$ 197.49$ 197.47$ 197.47$ 197.46$ 197.46$ 
-//END
-//START
-C514943013288152818G46+0HN166WXW00106"BV:11.8  198.02$ 203.26$ 204.69$ 205.24$ 200.09$ 198.97$ 198.54$ 198.31$ 198.17$ 198.07$ 198.00$ 197.94$ 
-//END
-//START
-C514943013288032818G46+0HN166WXW00107"BV:12.8  205.45$ 205.80$$ 200.50$ 199.34$ 198.89$ 198.62$ 198.43$ 198.31$ 198.23$ 198.16$ 198.11$ 198.06$ 
-//END
-//START
-C514943013287152818G46+0NN166WXW00106"BV:11.8  198.84$ 198.72$ 198.61$ 198.52$ 198.46$ 198.39$ 198.33$ 198.29$ 198.26$ 198.23$ 203.03$ 204.82$ 
-//END
-//START
-C514943013287032818G46+0NN166WXW00106"BV:12.6  198.63$ 198.60$ 198.54$ 198.48$ 198.44$ 198.39$ 204.52$ 205.43$ 203.46$ 199.82$ 199.28$ 199.02$ 
-//END
-//START
-C514943013286152818G46+0HN166WXW00106"BV:11.7  198.79$ 198.77$ 198.74$ 198.72$ 198.71$ 198.69$ 198.67$ 198.67$ 198.67$ 198.67$ 198.67$ 198.67$ 
-//END
-//START
-C514943013286032818G46+0HN166WXW00106"BV:12.6  199.37$ 199.27$ 199.18$ 199.10$ 199.03$ 198.98$ 198.93$ 198.89$ 198.87$ 198.85$ 198.83$ 198.81$ 
-//END
-//START
-C514943013285152818G47+0NN166WXW00106"BV:11.9  199.41$ 199.35$ 199.29$ 199.25$ 204.83$ 206.05$ 204.44$ 200.56$ 200.01$ 199.74$ 199.59$ 199.47$ 
-//END
-//START
-C514943013285032818G46+0HN166WXW00106"BV:12.4  205.57$ 206.51$ 206.94$ 201.59$ 200.56$ 200.16$ 199.94$ 199.77$ 199.67$ 199.59$ 199.52$ 199.47$ 
-//END
-//START
-C514943013284152818G46+0NN166WXW00106"BV:11.8  199.71$ 205.85$ 206.71$ 203.58$ 200.97$ 200.42$ 200.15$ 199.98$ 199.86$ 199.75$ 199.68$ 199.61$ 
-//END
-//START
-C514943013284032818G47+0HN166WXW00106"BV:12.6  206.77$ 207.24$ 202.46$ 201.04$ 200.57$ 200.31$ 200.15$ 200.02$ 199.94$ 199.86$ 199.80$ 199.75$ 
-//END
-//START
-C514943013283152818G46+0HN166WXW00106"BV:11.7  200.07$ 200.05$ 200.03$ 200.02$ 200.01$ 199.98$ 199.96$ 199.94$ 199.91$ 199.88$ 199.85$ 205.78$ 
-//END
-//START
-C514943013283032818G47+0NN166WXW00106"BV:12.4  201.08$ 200.88$ 200.73$ 200.60$ 200.51$ 200.42$ 200.33$ 200.27$ 200.21$ 200.16$ 200.12$ 200.10$ 
-//END
-//START
-C514943013282152818G45+0HN166WXW00106"BV:11.9  200.93$ 200.84$ 200.78$ 200.73$ 200.68$ 200.62$ 200.57$ 206.56$ 207.49$ 207.92$ 202.16$ 201.43$ 
-//END
-//START
-C514943013282032818G48+0HN166WXW00107"BV:12.6  201.14$ 201.06$ 201.00$$ 207.01$ 207.88$ 208.34$ 203.15$ 202.01$ 201.56$ 201.29$ 201.13$ 201.02$ 
-//END
-//START
-C514943013281152818G45+0HN166WXW00106"BV:11.9  201.28$ 201.24$ 206.22$ 207.84$ 208.45$ 203.71$ 202.32$ 201.88$ 201.62$ 201.46$ 201.31$ 201.22$ 
-//END
-//START
-C514943013281032818G48+0NN166WXW00106"BV:12.6  207.59$ 208.44$ 208.88$ 203.84$ 202.61$ 202.16$ 201.90$ 201.72$ 201.59$ 201.48$ 201.40$ 201.33$ 
-//END
-//START
-C514943013280152818G46+0HN166WXW00106"BV:11.9  201.69$ 201.68$ 201.66$ 201.64$ 201.60$ 201.56$ 201.51$ 201.47$ 201.43$ 201.39$ 201.38$ 201.36$ 
-//END
-//START
-C514943013280032818G47+0NN166WXW00106"BV:12.6  202.08$ 202.04$ 202.01$ 201.98$ 201.93$ 201.89$ 201.83$ 201.79$ 201.76$ 201.74$ 201.72$ 201.71$ 
-//END
-//START
-C514943013279152818G45+0HN166WXW00106"BV:11.9  204.38$ 203.45$ 203.07$ 202.85$ 202.68$ 202.56$ 202.44$ 202.32$ 202.25$ 202.18$ 202.13$ 202.10$ 
-//END
-//START
-C514943013279032818G47+0HN166WXW00106"BV:12.7  202.56$ 202.53$ 202.51$ 202.46$ 202.40$ 202.34$ 202.28$ 202.24$ 202.22$ 208.50$ 209.31$ 209.79$ 
-//END
-//START
-C514943013278152818G46+0HN166WXW00106"BV:11.8  205.32$ 204.06$ 203.63$ 203.38$ 203.20$ 203.06$ 202.95$ 202.84$ 202.76$ 202.69$ 202.64$ 202.60$ 
-//END
-//START
-C514943013278032818G46+0NN166WXW00106"BV:12.8  204.04$ 203.69$ 203.48$ 203.30$ 203.18$ 203.07$ 202.98$ 202.91$ 202.86$ 208.21$ 209.60$ 210.18$ 
-//END
-//START
-C514943013277152818G45+0NN166WXW00106"BV:11.7  203.34$ 203.29$ 203.25$ 203.20$ 203.14$ 203.08$ 203.03$ 203.00$ 209.27$ 210.07$ 210.51$ 204.83$ 
-//END
-//START
-C514943013277032818G46+0HN166WXW00106"BV:12.4  210.29$ 210.87$ 211.19$ 205.07$ 204.41$ 204.06$ 203.83$ 203.68$ 203.56$ 203.49$ 203.43$ 203.39$ 
-//END
-//START
-C514943013276152818G45+0NN166WXW00106"BV:11.8  204.14$ 204.06$ 203.99$ 203.93$ 203.85$ 203.78$ 203.73$ 203.68$ 203.63$ 203.61$ 203.59$ 208.77$ 
-//END
-//START
-C514943013276032818G46+0NN166WXW00106"BV:12.6  204.22$ 204.18$ 204.12$ 204.05$ 210.04$ 210.93$ 211.38$ 205.39$ 204.76$ 204.48$ 204.31$ 204.21$ 
-//END
-//START
-C514943013275152818G45+0HN166WXW00106"BV:11.9  205.39$ 205.08$ 204.89$ 204.75$ 204.64$ 204.54$ 204.47$ 204.40$ 204.35$ 204.31$ 204.28$ 204.26$ 
-//END
-//START
-C514943013275032818G46+0HN166WXW00106"BV:12.4  204.82$ 204.77$ 204.71$ 204.64$ 204.57$ 204.51$ 204.46$ 204.41$ 210.61$ 211.49$ 211.97$ 206.05$ 
-//END
-//START
-C514943013274152818G45+0NN166WXW00106"BV:11.7  206.47$ 205.91$ 205.61$ 205.43$ 205.28$ 205.18$ 205.09$ 205.03$ 204.98$ 204.94$ 204.90$ 204.87$ 
-//END
-//START
-C514943013274032818G46+0NN166WXW00106"BV:12.4  205.44$ 205.38$ 205.31$ 205.25$ 205.19$ 205.14$ 205.08$ 205.04$ 208.82$ 211.53$ 212.23$ 209.41$ 
-//END
-//START
-C514943013273152818G45+0NN166WXW00106"BV:11.9  206.20$ 206.04$ 205.94$ 205.83$ 205.76$ 205.69$ 205.63$ 205.59$ 205.56$ 205.53$ 205.50$ 205.47$ 
-//END
-//START
-C514943013273032818G46+0HN166WXW00108"BV:12.5  205.86$ 205.82$ 205.78$ 205.73$ 205.67$ 205.64$$ 208.52$ 212.04$ 212.79$$ 208.50$ 206.88$ 206.44$ 
-//END
-//START
-C514943013272152818G46+0NN166WXW00106"BV:11.7  206.15$ 206.10$ 206.07$ 206.01$ 205.99$ 205.95$ 205.93$ 205.92$ 205.90$ 205.88$ 205.88$ 205.88$ 
-//END
-//START
-C514943013272032818G45+0HN166WXW00108"BV:12.7  205.63$ 205.63$ 205.63$ 205.62$$$ 211.89$ 212.74$ 212.84$ 207.17$ 206.64$ 206.41$ 206.27$ 206.21$ 
-//END
-//START
-C514943013271152818G45+0HN166WXW00106"BV:11.7  205.83$ 205.75$ 205.69$ 205.65$ 205.62$ 205.60$ 205.59$ 205.59$ 205.60$ 205.60$ 205.62$ 205.63$ 
-//END
-//START
-C514943013271032818G45+0HN166WXW00106"BV:12.6  205.34$ 205.31$ 205.28$ 205.25$ 205.22$ 209.19$ 211.68$ 212.42$ 209.57$ 206.72$ 206.21$ 205.98$ 
-//END
-//START
-C514943013270152818G46+0HN166WXW00106"BV:11.9  212.19$ 209.00$ 206.52$ 206.01$ 205.76$ 205.62$ 205.53$ 205.48$ 205.44$ 205.41$ 205.39$ 205.37$ 
-//END
-//START
-C514943013270032818G45+0NN166WXW00106"BV:12.7  205.37$ 205.29$ 205.25$ 205.20$ 205.17$ 205.14$ 205.12$ 205.11$ 205.10$ 205.09$ 205.09$ 211.33$ 
-//END
-//START
-C514943013269152818G46+0HN166WXW00106"BV:11.9  204.99$ 204.95$ 204.92$ 204.90$ 204.89$ 210.98$ 211.87$ 209.43$ 206.34$ 205.81$ 205.58$ 205.46$ 
-//END
-//START
-C514943013269032818G45+0HN166WXW00106"BV:12.5  207.24$ 206.21$ 205.80$ 205.58$ 205.44$ 205.32$ 205.24$ 205.17$ 205.12$ 205.08$ 205.04$ 205.01$ 
-//END
-//START
-C514943013268152818G46+0NN166WXW00106"BV:11.9  205.57$ 205.52$ 205.47$ 205.42$ 205.37$ 205.31$ 205.27$ 205.23$ 205.20$ 210.57$ 211.84$ 212.40$ 
-//END
-//START
-C514943013268032818G45+0HN166WXW00106"BV:12.5  205.51$ 211.06$ 212.32$ 212.91$ 207.48$ 206.67$ 206.30$ 206.08$ 205.93$ 205.80$ 205.71$ 205.64$ 
-//END
-//START
-C514943013267152818G44+0NN166WXW00106"BV:11.9  205.93$ 205.88$ 205.81$ 205.77$ 205.74$ 205.69$ 205.64$ 205.59$ 205.55$ 205.51$ 205.49$ 205.49$ 
-//END
-//START
-C514943013267032818G42+0HN166WXW00106"BV:12.6  206.03$ 208.53$ 212.26$ 213.00$ 209.84$ 207.26$ 206.72$ 206.45$ 206.26$ 206.14$ 206.05$ 205.99$ 
-//END
-//START
-C514943013266152818G43+0HN166WXW00106"BV:11.9  206.69$ 206.63$ 206.57$ 206.52$ 206.47$ 206.41$ 206.33$ 206.27$ 206.22$ 206.17$ 206.12$ 206.07$ 
-//END
-//START
-C514943013266032818G42+0NN166WXW00106"BV:12.6  206.79$ 206.75$ 210.37$ 213.06$ 213.76$ 210.15$ 207.95$ 207.42$ 207.14$ 206.97$ 206.84$ 206.75$ 
-//END
-//START
-C514943013265152818G44+0NN166WXW00106"BV:11.9  207.35$ 207.31$ 207.27$ 207.23$ 207.18$ 207.12$ 207.06$ 207.00$ 206.95$ 206.90$ 206.86$ 206.82$ 
-//END
-//START
-C514943013265032818G42+0NN166WXW00106"BV:12.8  214.59$ 210.03$ 208.70$ 208.26$ 208.03$ 207.86$ 207.74$ 207.63$ 207.54$ 207.48$ 207.43$ 207.38$ 
-//END
-//START
-C514943013264152818G43+0HN166WXW00106"BV:11.9  208.05$ 208.03$ 208.00$ 207.95$ 207.89$ 207.83$ 207.77$ 207.72$ 207.67$ 207.63$ 210.57$ 213.84$ 
-//END
-//START
-C514943013264032818G42+0NN166WXW00106"BV:12.8  215.59$ 210.20$ 209.32$ 208.96$ 208.73$ 208.58$ 208.44$ 208.32$ 208.23$ 208.18$ 208.13$ 208.09$ 
-//END
-//START
-C514943013263152818G43+0HN166WXW00106"BV:11.9  208.77$ 208.74$ 208.69$ 208.63$ 208.56$ 208.50$ 208.43$ 208.37$ 208.30$ 208.27$ 214.21$ 215.12$ 
-//END
-//START
-C514943013263032818G42+0NN166WXW00107"BV:12.6  216.16$ 216.49$$ 211.06$ 210.11$ 209.70$ 209.45$ 209.25$ 209.11$ 209.01$ 208.94$ 208.87$ 208.82$ 
-//END
-//START
-C514943013262152818G44+0NN166WXW00106"BV:11.8  209.47$ 209.42$ 209.37$ 209.30$ 209.24$ 209.16$ 209.09$ 209.03$ 208.99$ 208.95$ 214.66$ 215.67$ 
-//END
-//START
-C514943013262032818G47+0NN166WXW00107"BV:12.6  215.83$ 216.60$$ 213.16$ 210.95$ 210.42$ 210.11$ 209.93$ 209.77$ 209.67$ 209.59$ 209.54$ 209.51$ 
-//END
-//START
-C514943013261152818G48+0NN166WXW00106"BV:11.8  210.93$ 210.71$ 210.54$ 210.41$ 210.27$ 210.15$ 210.05$ 209.97$ 209.89$ 209.83$ 209.79$ 209.76$ 
-//END
-//START
-C514943013261032818G47+0HN166WXW00106"BV:12.8  217.75$ 211.86$ 211.23$ 210.90$ 210.68$ 210.53$ 216.37$ 217.17$ 217.58$ 215.39$ 211.82$ 211.23$ 
-//END
-//START
-C514943013260152818G48+0HN166WXW00106"BV:11.8  210.59$ 210.55$ 210.51$ 210.47$ 210.40$ 210.33$ 210.29$ 210.25$ 210.23$ 215.62$ 216.86$ 217.41$ 
-//END
-//START
-C514943013260032818G47+0NN166WXW00106"BV:12.8  217.93$ 212.72$ 211.76$ 211.37$ 211.12$ 210.97$ 210.84$ 210.76$ 210.70$ 210.67$ 210.64$ 210.62$ 
-//END
-//START
-C514943013259152818G48+0HN166WXW00106"BV:11.9  217.05$ 217.57$ 212.14$ 211.37$ 211.05$ 210.86$ 210.74$ 210.66$ 210.61$ 213.91$ 216.80$ 217.54$ 
-//END
-//START
-C514943013259032818G47+0HN166WXW00106"BV:12.8  210.92$ 210.78$ 210.68$ 210.58$ 210.50$ 210.45$ 210.39$ 210.35$ 210.33$ 210.32$ 210.32$ 216.00$ 
-//END
-//START
-C514943013258152818G48+0HN166WXW00106"BV:11.8  210.49$ 210.46$ 210.42$ 210.38$ 210.35$ 210.32$ 215.71$ 216.93$ 217.52$ 212.03$ 211.35$ 211.08$ 
-//END
-//START
-C514943013258032818G47+0HN166WXW00106"BV:12.5  213.77$ 211.85$ 211.35$ 211.09$ 210.91$ 210.77$ 210.68$ 210.62$ 210.57$ 210.54$ 210.53$ 210.51$ 
-//END
-//START
-C514943013257152818G48+0HN166WXW00106"BV:11.8  210.75$ 210.72$ 210.67$ 210.63$ 210.60$ 210.56$ 210.54$ 210.53$ 210.53$ 214.66$ 216.76$ 217.43$ 
-//END
-//START
-C514943013257032818G47+0HN166WXW00106"BV:12.5  215.88$ 217.20$ 217.75$ 212.76$ 211.77$ 211.40$ 211.19$ 211.05$ 210.97$ 210.89$ 210.83$ 210.79$ 
-//END
-//START
-C514943013256152818G48+0NN166WXW00106"BV:11.9  212.24$ 211.82$ 211.59$ 211.44$ 211.30$ 211.21$ 211.14$ 211.08$ 211.02$ 210.99$ 210.95$ 210.90$ 
-//END
-//START
-C514943013256032818G47+0NN166WXW00106"BV:12.5  212.14$ 211.91$ 211.74$ 211.61$ 211.50$ 211.41$ 211.32$ 211.26$ 216.57$ 217.68$ 218.21$ 213.26$ 
-//END
-//START
-C514943013255152818G47+0NN166WXW00107"BV:11.8  211.82$ 211.76$ 211.72$ 211.67$ 211.63$ 211.59$ 211.55$ 215.78$ 217.69$ 218.35$$ 213.61$ 212.53$ 
-//END
-//START
-C514943013255032818G47+0HN166WXW00107"BV:12.5  218.77$ 219.15$$ 213.72$ 212.96$ 212.59$ 212.39$ 212.25$ 212.14$ 212.05$ 211.99$ 211.93$ 211.88$ 
-//END
-//START
-C514943013254152818G48+0HN166WXW00106"BV:11.9  212.60$ 212.50$ 212.42$ 212.34$ 212.29$ 212.24$ 212.18$ 212.13$ 212.09$ 212.04$ 215.43$ 218.07$ 
-//END
-//START
-C514943013254032818G47+0NN166WXW00106"BV:12.7  212.63$ 212.58$ 212.53$ 212.48$ 212.74$ 218.35$ 219.10$ 218.06$ 213.76$ 213.18$ 212.90$ 212.73$ 
-//END
-//START
-C514943013253152818G48+0NN166WXW00106"BV:11.9  219.74$ 220.13$ 214.47$ 213.82$ 213.52$ 213.30$ 213.15$ 213.03$ 212.93$ 212.83$ 212.75$ 212.69$ 
-//END
-//START
-C514943013253032818G47+0NN166WXW00106"BV:12.6  219.33$ 219.95$ 220.28$ 220.54$ 216.16$ 214.53$ 214.02$ 213.70$ 213.48$ 213.31$ 213.20$ 218.98$ 
-//END
-//START
-C514943013252152818G48+0HN166WXW00106"BV:11.9  213.89$ 213.70$ 213.57$ 213.49$ 213.42$ 213.35$ 213.28$ 213.23$ 213.18$ 213.14$ 213.10$ 217.77$ 
-//END
-//START
-C514943013252032818G48+0HN166WXW00107"BV:12.6  213.37$ 213.35$ 213.32$ 213.30$ 213.28$ 213.26$ 213.22$ 218.14$ 219.55$ 220.14$$ 215.08$ 214.22$ 
-//END
-//START
-C514943013251152818G48+0NN166WXW00106"BV:11.9  213.23$ 219.12$ 219.97$ 220.42$ 214.80$ 214.19$ 213.88$ 213.70$ 213.58$ 213.51$ 213.45$ 213.40$ 
-//END
-//START
-C514943013251032818G47+0NN166WXW00106"BV:12.5  215.26$ 214.26$ 213.90$ 213.71$ 213.56$ 213.48$ 213.40$ 213.33$ 213.29$ 213.27$ 213.25$ 213.23$ 
-//END
-//START
-C514943013250152818G47+0NN166WXW00106"BV:12.0  213.23$ 213.20$ 213.18$ 213.17$ 213.14$ 213.11$ 213.09$ 213.06$ 213.05$ 218.38$ 219.55$ 220.09$ 
-//END
-//START
-C514943013250032818G47+0NN166WXW00106"BV:12.4  212.99$ 212.94$ 212.90$ 217.27$ 219.07$ 219.69$ 216.12$ 214.14$ 213.67$ 213.47$ 213.34$ 213.27$ 
-//END
-//START
-C514943013249152818G48+0NN166WXW00106"BV:11.9  212.45$ 212.46$ 212.48$ 212.48$ 216.91$ 218.72$ 219.36$ 215.21$ 213.75$ 213.33$ 213.14$ 213.04$ 
-//END
-//START
-C514943013249032818G48+0HN166WXW00106"BV:12.5  212.38$ 218.20$ 219.02$ 213.78$ 213.07$ 212.77$ 212.63$ 212.53$ 212.48$ 212.45$ 212.44$ 212.44$ 
-//END
-//START
-C514943013248152818G47+0NN166WXW00107"BV:11.9  212.06$ 212.02$ 212.00$ 211.97$$ 216.63$ 218.24$ 218.85$ 214.47$ 213.12$ 212.73$ 212.55$ 212.45$ 
-//END
-//START
-C514943013248032818G48+0NN166WXW00107"BV:12.5  211.45$ 215.53$ 217.63$ 218.29$ 218.67$ 218.92$ 219.13$$ 213.32$ 212.67$ 212.36$ 212.20$ 212.12$ 
-//END
-//START
-C514943013247152818G47+0HN166WXW00106"BV:12.0  211.19$ 216.44$ 217.66$ 218.20$ 213.24$ 212.26$ 211.90$ 211.70$ 211.59$ 211.53$ 211.49$ 211.47$ 
-//END
-//START
-C514943013247032818G47+0NN166WXW00106"BV:12.6  210.72$ 216.03$ 217.28$ 217.85$ 218.21$ 212.88$ 212.05$ 211.68$ 211.47$ 211.33$ 211.26$ 211.23$ 
-//END
-//START
-C514943013246152818G48+0NN166WXW00106"BV:11.9  210.37$ 215.07$ 216.70$ 217.31$ 212.70$ 211.49$ 211.09$ 210.89$ 210.76$ 210.69$ 210.68$ 210.69$ 
-//END
-//START
-C514943013246032818G48+0HN166WXW00106"BV:12.5  215.39$ 216.60$ 217.14$ 217.46$ 211.78$ 211.08$ 210.75$ 210.57$ 210.47$ 210.42$ 210.39$ 210.38$ 
-//END
-//START
-C514943013245152818G48+0HN166WXW00106"BV:12.0  212.51$ 211.25$ 210.83$ 210.60$ 210.46$ 210.34$ 210.27$ 210.22$ 210.18$ 210.17$ 210.15$ 210.14$ 
-//END
-//START
-C514943013245032818G48+0HN166WXW00106"BV:12.5  210.85$ 210.55$ 210.38$ 210.25$ 210.14$ 210.06$ 210.01$ 209.97$ 209.94$ 215.71$ 216.63$ 217.12$ 
-//END
-//START
-C514943013244152818G48+0NN166WXW00106"BV:12.0  209.94$ 209.87$ 209.80$ 209.77$ 209.73$ 209.71$ 209.69$ 209.68$ 215.49$ 216.43$ 216.94$ 211.58$ 
-//END
-//START
-C514943013244032818G48+0NN166WXW00106"BV:12.5  209.95$ 209.82$ 209.73$ 213.50$ 215.80$ 216.47$ 216.84$ 211.35$ 210.60$ 210.28$ 210.11$ 210.01$ 
-//END
-//START
-C514943013243152818G48+0HN166WXW00106"BV:12.0  209.66$ 209.61$ 209.56$ 209.52$ 209.49$ 214.56$ 215.92$ 216.49$ 212.24$ 210.74$ 210.30$ 210.08$ 
-//END
-//START
-C514943013243032818G47+0NN166WXW00108"BV:12.5  209.57$ 209.52$ 209.48$ 215.23$ 216.11$ 216.56$$$ 211.17$ 210.39$ 210.08$ 209.91$ 209.79$ 209.72$ 
-//END
-//START
-C514943013242152818G47+0NN166WXW00106"BV:12.0  209.48$ 209.45$ 215.35$ 216.20$ 213.48$ 210.75$ 210.23$ 209.99$ 209.84$ 209.75$ 209.68$ 209.63$ 
-//END
-//START
-C514943013242032818G47+0NN166WXW00106"BV:12.5  209.43$ 215.34$ 216.15$ 216.57$ 210.84$ 210.21$ 209.94$ 209.75$ 209.65$ 209.59$ 209.54$ 209.51$ 
-//END
-//START
-C514943013241152818G47+0NN166WXW00106"BV:12.0  210.60$ 210.22$ 210.01$ 209.87$ 209.76$ 209.69$ 209.63$ 209.59$ 209.55$ 209.53$ 209.50$ 209.46$ 
-//END
-//START
-C514943013241032818G47+0NN166WXW00106"BV:12.7  209.94$ 209.82$ 209.73$ 209.65$ 209.58$ 209.52$ 209.47$ 209.43$ 215.24$ 216.14$ 216.61$ 211.63$ 
-//END
-//START
-C514943013240152818G47+0HN166WXW00106"BV:12.0  209.73$ 209.69$ 209.65$ 209.63$ 209.60$ 215.56$ 216.38$ 216.83$ 211.19$ 210.54$ 210.24$ 210.06$ 
-//END
-//START
-C514943013240032818G48+0HN166WXW00107"BV:12.7  216.90$ 217.25$$ 211.46$ 210.82$ 210.51$ 210.30$ 210.17$ 210.06$ 209.97$ 209.89$ 209.81$ 209.76$ 
-//END
-//START
-C514943013239152818G48+0NN166WXW00106"BV:12.0  210.35$ 210.27$ 210.21$ 210.17$ 210.13$ 210.10$ 210.07$ 210.04$ 210.02$ 210.00$ 214.66$ 216.28$ 
-//END
-//START
-C514943013239032818G48+0NN166WXW00108"BV:12.9  210.21$ 210.17$ 210.13$ 210.10$ 210.06$$ 215.98$ 216.81$ 216.15$$ 211.45$ 210.89$ 210.61$ 210.47$ 
-//END
-//START
-C514943013238152818G47+0HN166WXW00106"BV:12.0  216.35$ 217.06$ 217.46$ 211.68$ 211.07$ 210.78$ 210.62$ 210.50$ 210.41$ 210.34$ 210.29$ 210.25$ 
-//END
-//START
-C514943013238032818G46+0HN166WXW00106"BV:12.5  209.82$ 209.78$ 215.60$ 216.50$ 216.93$ 217.24$ 217.47$ 211.61$ 210.96$ 210.63$ 210.46$ 212.50$ 
-//END
-//START
-C514943013237152818G47+0HN166WXW00106"BV:12.0  215.86$ 216.61$ 217.03$ 212.86$ 211.17$ 210.66$ 210.39$ 210.21$ 210.08$ 210.00$ 209.93$ 209.87$ 
-//END
-//START
-C514943013237032818G48+0NN166WXW00106"BV:12.6  209.60$ 209.56$ 214.91$ 216.10$ 216.63$ 215.55$ 211.06$ 210.47$ 210.17$ 210.00$ 209.90$ 211.57$ 
-//END
-//START
-C514943013236152818G47+0NN166WXW00106"BV:12.0  216.57$ 213.53$ 216.90$ 211.60$ 210.75$ 210.38$ 210.14$ 209.99$ 209.87$ 209.76$ 209.70$ 209.64$ 
-//END
-//START
-C514943013236032818G48+0NN166WXW00106"BV:12.6  214.17$ 215.86$ 216.47$ 216.79$ 217.02$ 212.44$ 210.90$ 210.37$ 210.08$ 209.91$ 213.27$ 215.89$ 
-//END
-//START
-C514943013235152818G47+0HN166WXW00106"BV:12.0  209.90$ 209.79$ 214.83$ 216.17$ 216.72$ 211.80$ 210.69$ 210.27$ 210.03$ 209.86$ 209.73$ 209.64$ 
-//END
-//START
-C514943013235032818G46+0NN166WXW00106"BV:12.8  209.87$ 209.79$ 209.74$ 209.67$ 214.59$ 216.01$ 216.55$ 216.86$ 211.46$ 210.58$ 210.22$ 210.02$ 
-//END
-//START
-C514943013234152818G47+0NN166WXW00106"BV:11.9  209.80$ 209.77$ 209.75$ 215.38$ 216.38$ 216.85$ 211.53$ 210.69$ 210.33$ 210.14$ 210.02$ 209.95$ 
-//END
-//START
-C514943013234032818G47+0HN166WXW00106"BV:12.7  215.40$ 216.45$ 216.93$ 212.44$ 211.03$ 210.56$ 210.31$ 210.17$ 210.03$ 209.95$ 209.89$ 209.85$ 
-//END
-//START
-C514943013233152818G47+0NN166WXW00106"BV:12.0  217.04$ 213.32$ 211.25$ 210.71$ 210.42$ 210.21$ 210.07$ 209.97$ 209.89$ 209.82$ 209.79$ 209.77$ 
-//END
-//START
-C514943013233032818G47+0HN166WXW00106"BV:12.7  215.38$ 216.29$ 216.75$ 217.05$ 217.26$ 212.01$ 210.94$ 210.49$ 210.22$ 210.05$ 215.64$ 216.58$ 
-//END
-//START
-C514943013232152818G47+0NN166WXW00106"BV:12.0  217.00$ 217.23$ 212.05$ 210.95$ 210.48$ 210.19$ 210.00$ 209.84$ 209.74$ 209.67$ 209.62$ 209.59$ 
-//END
-//START
-C514943013232032818G47+0NN166WXW00106"BV:12.7  215.33$ 216.16$ 216.62$ 216.93$ 212.43$ 210.91$ 210.39$ 210.08$ 209.88$ 214.59$ 216.08$ 216.65$ 
-//END
-//START
-C514943013231152818G48+0NN166WXW00106"BV:12.1  215.84$ 216.29$ 211.26$ 210.19$ 209.77$ 209.54$ 209.38$ 209.27$ 209.21$ 209.16$ 209.15$ 209.82$ 
-//END
-//START
-C514943013231032818G45+0HN166WXW00106"BV:12.6  208.71$ 213.82$ 215.19$ 215.74$ 216.06$ 216.29$ 211.10$ 210.00$ 209.57$ 209.33$ 209.21$ 214.90$ 
-//END
-//START
-C514943013230152818G47+0NN166WXW00106"BV:12.1  215.77$ 216.10$ 210.67$ 209.76$ 209.39$ 209.16$ 209.01$ 208.91$ 208.83$ 208.79$ 208.76$ 208.73$ 
-//END
-//START
-C514943013230032818G47+0HN166WXW00108"BV:12.7 $$ 212.73$ 214.65$ 215.28$ 215.63$ 215.87$ 210.54$ 209.52$ 209.10$ 208.88$ 208.75$ 214.00$ 215.24$ 
-//END
-//START
-C514943013229152818G48+0HN166WXW00107"BV:12.1  215.85$$ 210.28$ 209.40$ 209.01$ 208.76$ 208.61$ 208.51$ 208.43$ 208.39$ 208.37$ 208.34$ 208.31$ 
-//END
-//START
-C514943013229032818G47+0HN166WXW00109"BV:12.6  216.31$ 216.31$ 216.31$ 216.32$ 216.32$ 210.85$$$ 208.60$ 208.39$$ 212.49$ 214.57$ 215.22$ 215.59$ 
-//END
-//START
-C514943013228152818G48+0NN166WXW00106"BV:12.1  216.32$ 216.32$ 216.32$ 216.32$ 216.32$ 216.32$ 216.32$ 216.32$ 216.32$ 216.32$ 216.31$ 216.31$ 
-//END
-//START
-C514943013228032818G46+0HN166WXW00106"BV:12.8  216.13$ 216.13$ 216.45$ 216.72$ 216.37$ 216.02$ 216.02$ 216.02$ 216.02$ 216.02$ 216.02$ 216.02$ 
-//END
-//START
-C514943013227152818G42+0HN166WXW00106"BV:12.1  216.26$ 216.61$ 216.13$ 216.13$ 216.13$ 216.13$ 216.13$ 216.13$ 216.13$ 216.13$ 216.13$ 216.13$ 
-//END
-//START
-C514943013227032818G43+0NN166WXW00106"BV:12.6  215.79$ 215.78$ 216.14$ 216.13$ 216.13$ 216.13$ 216.13$ 216.13$ 216.13$ 216.13$ 216.13$ 216.13$ 
-//END
-//START
-C514943013226152818G42+0NN166WXW00106"BV:12.0  215.50$ 215.50$ 215.50$ 215.50$ 215.50$ 215.50$ 215.50$ 215.50$ 215.50$ 215.50$ 215.50$ 215.50$ 
-//END
-//START
-C514943013226032818G43+0HN166WXW00106"BV:12.6  215.50$ 215.50$ 215.50$ 215.50$ 215.50$ 215.50$ 215.50$ 215.50$ 215.50$ 215.50$ 215.50$ 215.50$ 
-//END
-//START
-C514943013225152818G42+0HN166WXW00106"BV:12.0  215.50$ 215.50$ 215.50$ 215.50$ 215.50$ 215.50$ 215.50$ 215.50$ 215.50$ 215.50$ 215.50$ 215.50$ 
-//END
-//START
-C514943013225032818G47+0NN166WXW00106"BV:12.6  214.31$ 214.31$ 214.32$ 214.42$ 214.42$ 214.43$ 214.43$ 214.43$ 214.43$ 215.50$ 215.50$ 215.50$ 
-//END
-//START
-C514943013224152818G48+0NN166WXW00106"BV:12.0  214.31$ 214.31$ 214.31$ 214.31$ 214.31$ 214.31$ 214.31$ 214.31$ 214.31$ 214.31$ 214.31$ 214.31$ 
-//END
-//START
-C514943013224032818G48+0NN166WXW00106"BV:12.7  214.31$ 214.31$ 214.31$ 214.31$ 214.31$ 214.31$ 214.31$ 214.31$ 214.31$ 214.31$ 214.31$ 214.31$ 
-//END
-//START
-C514943013223152818G48+0HN166WXW00106"BV:12.1  214.13$ 214.31$ 214.31$ 214.31$ 214.31$ 214.31$ 214.31$ 214.31$ 214.31$ 214.31$ 214.31$ 214.31$ 
-//END
-//START
-C514943013223032818G47+0HN166WXW00106"BV:12.6  213.84$ 213.84$ 213.84$ 213.95$ 214.14$ 214.13$ 214.13$ 214.13$ 214.13$ 214.13$ 214.13$ 214.13$ 
-//END
-//START
-C514943013222152818G48+0HN166WXW00106"BV:12.1  213.53$ 213.83$ 213.84$ 213.84$ 213.84$ 213.84$ 213.84$ 213.84$ 213.84$ 213.84$ 213.84$ 213.84$ 
-//END
-//START
-C514943013222032818G47+0NN166WXW00106"BV:12.7  212.90$ 212.90$ 212.90$ 213.18$ 213.41$ 213.41$ 213.41$ 213.41$ 213.41$ 213.41$ 213.41$ 213.41$ 
-//END
-//START
-C514943013221152818G48+0HN166WXW00106"BV:12.1  212.50$ 212.66$ 212.90$ 212.90$ 212.90$ 212.90$ 212.90$ 212.90$ 212.90$ 212.90$ 212.90$ 212.90$ 
-//END
-//START
-C514943013221032818G48+0HN166WXW00106"BV:12.8  211.98$ 211.98$ 211.98$ 212.17$ 212.42$ 212.50$ 212.50$ 212.50$ 212.50$ 212.50$ 212.50$ 212.50$ 
-//END
-//START
-C514943013220152818G47+0HN166WXW00106"BV:12.1  211.19$ 211.75$ 211.98$ 211.98$ 211.98$ 211.98$ 211.98$ 211.98$ 211.98$ 211.98$ 211.98$ 211.98$ 
-//END
-//START
-C514943013220032818G48+0NN166WXW00106"BV:12.7  209.54$ 210.88$ 211.51$ 211.87$ 206.27$ 205.38$ 205.00$ 204.76$ 204.60$ 204.50$ 204.44$ 210.02$ 
-//END
-//START
-C514943013219152818G48+0NN166WXW00106"BV:12.1  210.72$ 211.26$ 206.16$ 205.10$ 204.70$ 204.46$ 204.31$ 204.19$ 204.09$ 204.06$ 204.03$ 204.04$ 
-//END
-//START
-C514943013219032818G48+0NN166WXW00106"BV:12.7  209.20$ 210.44$ 210.99$ 211.33$ 205.59$ 204.79$ 204.42$ 204.20$ 204.06$ 203.97$ 203.92$ 209.59$ 
-//END
-//START
-C514943013218152818G48+0HN166WXW00107"BV:12.1  210.56$ 211.03$$ 205.29$ 204.60$ 204.29$ 204.10$ 203.98$ 203.89$ 203.83$ 203.78$ 203.75$ 203.72$ 
-//END
-//START
-C514943013218032818G47+0HN166WXW00108"BV:12.7  208.57$ 209.81$ 210.37$ 210.73$$$ 205.29$ 204.32$ 203.92$ 203.71$ 203.59$ 203.52$ 203.49$ 209.69$ 
-//END
-//START
-C514943013217152818G48+0HN166WXW00106"BV:12.1  209.58$ 210.13$ 205.38$ 204.06$ 203.63$ 203.40$ 203.26$ 203.17$ 203.11$ 203.07$ 203.05$ 203.04$ 
-//END
-//START
-C514943013217032818G47+0HN166WXW00107"BV:12.7  202.39$$ 207.41$ 209.00$ 209.62$ 206.01$ 203.77$ 203.24$ 202.98$ 202.83$ 202.76$ 202.72$ 208.42$ 
-//END
-//START
-C514943013216152818G48+0NN166WXW00106"BV:12.1  209.64$ 209.99$ 204.13$ 203.38$ 203.03$ 202.82$ 202.67$ 202.56$ 202.50$ 202.46$ 202.43$ 202.40$ 
-//END
-//START
-C514943013216032818G47+0NN166WXW00106"BV:12.7  206.83$ 208.38$ 209.00$ 209.37$ 209.61$ 204.40$ 203.21$ 202.76$ 202.52$ 202.38$ 207.94$ 209.10$ 
-//END
-//START
-C514943013215152818G49+0NN166WXW00106"BV:12.1  208.90$ 203.94$ 202.74$ 202.33$ 202.10$ 201.96$ 201.87$ 201.82$ 201.78$ 201.76$ 201.75$ 201.74$ 
-//END
-//START
-C514943013215032818G48+0HN166WXW00107"BV:12.7  206.47$ 207.82$ 208.39$ 208.73$ 208.98$$ 203.25$ 202.36$ 201.99$ 201.77$ 201.65$ 207.22$ 208.36$ 
-//END
-//START
-C514943013214152818G48+0HN166WXW00106"BV:12.1  207.49$ 208.06$ 208.42$ 202.36$ 201.74$ 201.45$ 201.28$ 201.18$ 201.13$ 201.10$ 201.10$ 201.08$ 
-//END
-//START
-C514943013214032818G48+0NN166WXW00106"BV:12.6  200.31$ 200.30$ 206.35$ 207.27$ 207.76$ 202.06$ 201.32$ 201.02$ 200.85$ 200.77$ 200.71$ 206.23$ 
-//END
-//START
-C514943013213152818G48+0HN166WXW00107"BV:12.1 $ 203.48$ 206.55$ 207.29$ 202.74$ 201.29$ 200.85$ 200.63$ 200.50$ 200.44$ 200.39$ 200.35$ 200.33$ 
-//END
-//START
-C514943013213032818G48+0HN166WXW00106"BV:12.7  199.85$ 199.81$ 199.77$ 199.73$ 203.95$ 206.20$ 206.91$ 204.07$ 201.19$ 200.65$ 200.41$ 200.28$ 
-//END
-//START
-C514943013212152818G48+0NN166WFF00106"BV:12.1  199.38 199.37 199.36 205.03 206.24 206.82 201.24 200.49 200.20 200.06 199.97 199.90 
-//END
-//START
-C514943013212032818G48+0NN166WFF00106"BV:12.9  199.25 198.78 198.75 198.73 205.03 205.88 204.20 200.31 199.78 199.56 199.46 199.41 
-//END
-//START
-C514943013211152818G48+0NN166WXW00106"BV:12.3  198.89$ 198.85$ 205.10$ 205.99$ 204.31$ 200.39$ 199.85$ 199.61$ 199.48$ 199.40$ 199.34$ 199.29$ 
-//END
-//START
-C514943013211032818G47+0HN166WXW00106"BV:12.9  198.21$ 198.26$ 202.06$ 204.88$ 205.65$ 202.50$ 199.89$ 199.38$ 199.14$ 199.01$ 198.94$ 198.91$ 
-//END
-//START
-C514943013210152818G48+0HN166WXW00106"BV:12.3  205.39$ 199.79$ 198.91$ 198.56$ 198.38$ 198.27$ 198.20$ 198.15$ 198.14$ 198.12$ 198.12$ 198.15$ 
-//END
-//START
-C514943013210032818G47+0HN166WXW00106"BV:12.5  198.63$ 197.99$ 197.72$ 197.57$ 197.48$ 197.42$ 197.39$ 197.38$ 197.37$ 203.37$ 204.45$ 205.01$ 
-//END
-//START
-C514943013209152818G48+0NN166WXW00106"BV:11.9  196.67$ 196.67$ 196.69$ 196.71$ 196.73$ 196.75$ 196.77$ 196.79$ 196.81$ 203.01$ 204.00$ 204.52$ 
-//END
-//START
-C514943013209032818G47+0HN166WXW00107"BV:12.7  196.20$ 196.19$$ 202.16$ 203.24$ 203.78$ 197.92$ 197.24$ 196.96$ 196.82$ 196.75$ 196.71$ 196.68$ 
-//END
-//START
-C514943013208152818G48+0HN166WXW00106"BV:12.4  200.80$ 202.62$ 203.32$ 198.00$ 197.06$ 196.73$ 196.53$ 196.43$ 196.36$ 196.31$ 196.26$ 196.23$ 
-//END
-//START
-C514943013208032818G47+0NN166WXW00106"BV:13.1  195.40$ 195.40$ 195.39$ 201.53$ 202.65$ 203.22$ 197.07$ 196.44$ 196.16$ 196.02$ 195.95$ 195.91$ 
-//END
-//START
-C514943013207152818G48+0HN166WXW00106"BV:11.9  202.47$ 201.93$ 196.73$ 196.14$ 195.87$ 195.71$ 195.61$ 195.54$ 195.48$ 195.45$ 195.49$ 195.41$ 
-//END
-//START
-C514943013207032818G47+0NN166WXW00106"BV:13.1  194.32$ 200.48$ 201.52$ 202.07$ 202.43$ 198.17$ 196.21$ 195.67$ 195.38$ 195.21$ 199.79$ 201.78$ 
-//END
-Max data limit reached (25000)
-</PRE>
diff --git a/test/files/usgs/eddn/C5149430_file3.txt b/test/files/usgs/eddn/C5149430_file3.txt
deleted file mode 100644
index e284abbf..00000000
--- a/test/files/usgs/eddn/C5149430_file3.txt
+++ /dev/null
@@ -1,32 +0,0 @@
-<PRE>
-//START
-C514943013206152818G48+0HN166WXW00106"BV:12.3  194.99$ 194.71$ 194.57$ 194.48$ 194.42$ 194.37$ 194.34$ 194.32$ 194.32$ 194.31$ 194.31$ 194.31$ 
-//END
-//START
-C514943013206032818G47+0NN166WXW00106"BV:12.9  194.08$ 194.06$ 194.03$ 194.00$ 193.96$ 193.93$ 193.89$ 198.53$ 200.55$ 201.25$ 201.67$ 195.62$ 
-//END
-//START
-C514943013205152818G48+0HN166WXW00106"BV:11.9  193.95$ 193.95$ 193.95$ 200.26$ 201.15$ 197.01$ 195.21$ 194.73$ 194.49$ 194.32$ 194.21$ 194.14$ 
-//END
-//START
-C514943013205032818G47+0NN166WXW00106"BV:12.6  201.45$ 196.09$ 195.10$ 194.72$ 194.50$ 194.34$ 194.22$ 194.12$ 194.04$ 194.00$ 193.96$ 193.95$ 
-//END
-//START
-C514943013204152818G48+0HN166WUB00106"BV:12.0  194.13$ 194.12$ 194.11$ 194.08$ 194.05$ 194.02$ 193.99$ 193.96$ 193.94$ 193.92$ 199.51$ 200.86$ 
-//END
-//START
-C514943013204032818G48+0HN166WXW00108"BV:12.9 $$ 197.32$ 200.66$ 201.44$ 196.58$ 195.27$ 194.82$ 194.57$ 194.40$ 194.28$ 194.21$ 194.16$ 194.14$ 
-//END
-//START
-C514943013203152818G49+0NN166WXW00106"BV:12.0  194.52$ 194.47$ 194.43$ 194.38$ 194.33$ 194.29$ 194.24$ 194.21$ 194.19$ 194.17$ 194.16$ 194.15$ 
-//END
-//START
-C514943013203032818G48+0NN166WXW00107"BV:12.5  194.49$ 194.47$ 194.43$ 194.39$ 199.91$ 201.21$$ 199.89$ 195.62$ 195.05$ 194.79$ 194.65$ 194.57$ 
-//END
-//START
-C514943013202152818G50+0NN166WXW00106"BV:11.9  194.90$ 194.85$ 194.81$ 194.76$ 194.70$ 194.64$ 194.60$ 194.56$ 194.54$ 194.52$ 194.52$ 194.50$ 
-//END
-//START
-C514943013202032818G48+0NN166WXW00106"BV:13.0  195.01$ 194.97$ 194.92$ 201.19$ 202.01$ 199.10$ 196.16$ 195.58$ 195.30$ 195.13$ 195.02$ 194.96$ 
-//END
-</PRE>
diff --git a/test/files/usgs/eddn/C514D73A.txt b/test/files/usgs/eddn/C514D73A.txt
deleted file mode 100644
index aecc710b..00000000
--- a/test/files/usgs/eddn/C514D73A.txt
+++ /dev/null
@@ -1,62 +0,0 @@
-<PRE>
-//START
-C514D73A13305072816G47-0NN166WXW00122"
--445.51 
--445.52 
--445.52 
--445.51 
--445.50 
--445.49 
--445.47 
--445.47 
--445.47 
--445.48 
--445.49 
--445.49  
-//END
-//START
-C514D73A13304192816G46-0NN166WXW00117"
--445.50 
--445.49 
--445.49 
--445.49 
-// 
--445.49 
--445.49 
--445.49 
--445.51 
--445.52 
--445.53 
--445.54  
-//END
-//START
-C514D73A13304072816G47+0NN166WXW00122"
--445.56 
--445.56 
--445.56 
--445.55 
--445.55 
--445.52 
--445.51 
--445.50 
--445.50 
--445.49 
--445.50 
--445.50  
-//END
-//START
-C514D73A13303192816G46-0NN166WXW00122"
--445.50 
--445.50 
--445.50 
--445.50 
--445.50 
--445.50 
--445.48 
--445.47 
--445.48 
--445.47 
--445.48 
--445.48  
-//END
-</PRE>
diff --git a/test/files/usgs/eddn/C516C1B8.txt b/test/files/usgs/eddn/C516C1B8.txt
deleted file mode 100644
index faf52577..00000000
--- a/test/files/usgs/eddn/C516C1B8.txt
+++ /dev/null
@@ -1,86 +0,0 @@
-<PRE>
-//START
-C516C1B813305134352G49-1NN138WXW00236"BV:12.6  Channel:5 Time:3 +295.77 +296.07 +296.00 +296.00 +296.00 +295.92 +295.92 +295.77 +296.07 +295.77 +296.00 +295.85 Channel:6 Time:3 +316.20 +316.42 +316.50 +316.57 +316.50 +316.50 +316.50 +316.57 +316.42 +316.50 +316.20 +316.35 
-//END
-//START
-C516C1B813305014352G48-1NN138WXW00236"BV:12.5  Channel:5 Time:3 +295.63 +295.77 +295.77 +295.63 +295.55 +295.55 +295.70 +295.48 +295.55 +295.55 +295.63 +295.63 Channel:6 Time:3 +315.98 +316.05 +315.91 +315.83 +315.91 +315.83 +315.91 +315.83 +315.83 +315.76 +315.91 +316.05 
-//END
-//START
-C516C1B813304134352G48-1NN138WXW00236"BV:12.6  Channel:5 Time:3 +295.55 +295.92 +295.77 +295.92 +296.00 +295.92 +295.92 +295.92 +295.85 +295.77 +295.77 +295.85 Channel:6 Time:3 +315.76 +316.13 +315.98 +316.05 +316.13 +315.98 +316.13 +316.20 +316.13 +315.98 +316.05 +316.05 
-//END
-//START
-C516C1B813304014352G48-1NN138WXW00236"BV:12.5  Channel:5 Time:3 +295.48 +295.63 +295.63 +295.55 +295.55 +295.63 +295.70 +295.63 +295.63 +295.70 +295.63 +295.63 Channel:6 Time:3 +315.69 +315.83 +315.76 +315.76 +315.69 +315.91 +315.91 +315.91 +315.83 +315.83 +315.91 +315.91 
-//END
-//START
-C516C1B813303134352G47-1NN138WXW00236"BV:12.7  Channel:5 Time:3 +295.55 +295.70 +295.70 +295.70 +295.70 +295.70 +295.63 +295.55 +295.63 +295.70 +295.55 +295.55 Channel:6 Time:3 +315.61 +315.91 +315.83 +315.91 +315.83 +315.91 +315.91 +315.83 +315.83 +315.76 +315.69 +315.76 
-//END
-//START
-C516C1B813303014352G47-1NN138WXW00236"BV:12.5  Channel:5 Time:3 +295.33 +295.55 +295.55 +295.55 +295.48 +295.48 +295.48 +295.48 +295.55 +295.55 +295.63 +295.63 Channel:6 Time:3 +315.61 +315.61 +315.61 +315.54 +315.54 +315.54 +315.61 +315.69 +315.61 +315.69 +315.76 +315.69 
-//END
-//START
-C516C1B813302134352G48-1NN138WXW00236"BV:12.7  Channel:5 Time:3 +295.26 +295.70 +295.70 +295.70 +295.63 +295.70 +295.70 +295.55 +295.70 +295.70 +295.70 +295.70 Channel:6 Time:3 +315.54 +315.69 +315.69 +315.69 +315.69 +315.76 +315.69 +315.69 +315.69 +315.69 +315.69 +315.61 
-//END
-//START
-C516C1B813302014352G47-1NN138WXW00236"BV:12.4  Channel:5 Time:3 +295.48 +295.63 +295.63 +295.63 +295.41 +295.48 +295.41 +295.48 +295.55 +295.63 +295.63 +295.70 Channel:6 Time:3 +315.46 +315.61 +315.46 +315.39 +315.39 +315.39 +315.39 +315.46 +315.54 +315.54 +315.54 +315.69 
-//END
-//START
-C516C1B813301134352G47-1NN138WXW00236"BV:12.6  Channel:5 Time:3 +295.48 +295.70 +295.70 +295.77 +295.70 +295.70 +295.85 +295.70 +295.77 +295.85 +295.77 +295.77 Channel:6 Time:3 +315.39 +315.61 +315.61 +315.54 +315.61 +315.61 +315.61 +315.69 +315.61 +315.69 +315.69 +315.61 
-//END
-//START
-C516C1B813301014352G47-1NN138WXW00236"BV:12.4  Channel:5 Time:3 +295.55 +295.77 +295.70 +295.55 +295.63 +295.63 +295.55 +295.63 +295.63 +295.63 +295.70 +295.77 Channel:6 Time:3 +315.32 +315.61 +315.54 +315.39 +315.39 +315.39 +315.39 +315.46 +315.46 +315.46 +315.54 +315.54 
-//END
-//START
-C516C1B813300134352G48-1NN138WXW00236"BV:12.6  Channel:5 Time:3 +295.41 +295.70 +295.77 +295.77 +295.77 +295.70 +295.70 +295.63 +295.70 +295.63 +295.70 +295.55 Channel:6 Time:3 +315.32 +315.61 +315.61 +315.61 +315.54 +315.54 +315.46 +315.46 +315.46 +315.46 +315.46 +315.46 
-//END
-//START
-C516C1B813300014352G47-1NN138WXW00236"BV:12.4  Channel:5 Time:3 +295.48 +295.55 +295.48 +295.48 +295.48 +295.33 +295.41 +295.33 +295.55 +295.48 +295.55 +295.48 Channel:6 Time:3 +315.24 +315.39 +315.39 +315.32 +315.17 +315.24 +315.24 +315.24 +315.32 +315.39 +315.46 +315.54 
-//END
-//START
-C516C1B813299134352G48-1NN138WXW00236"BV:12.6  Channel:5 Time:3 +295.41 +295.48 +295.55 +295.55 +295.63 +295.55 +295.55 +295.55 +295.55 +295.48 +295.48 +295.48 Channel:6 Time:3 +315.32 +315.54 +315.61 +315.46 +315.54 +315.54 +315.54 +315.61 +315.46 +315.61 +315.54 +315.39 
-//END
-//START
-C516C1B813299014352G47-1NN138WXW00236"BV:12.4  Channel:5 Time:3 +295.33 +295.41 +295.41 +295.26 +295.26 +295.11 +295.04 +295.11 +295.11 +295.11 +295.26 +295.41 Channel:6 Time:3 +315.17 +315.46 +315.32 +315.32 +315.17 +315.10 +315.17 +315.10 +315.17 +315.24 +315.39 +315.46 
-//END
-//START
-C516C1B813298134352G48-1NN138WXW00236"BV:12.6  Channel:5 Time:3 +295.33 +295.48 +295.55 +295.48 +295.63 +295.63 +295.48 +295.63 +295.55 +295.63 +295.55 +295.55 Channel:6 Time:3 +315.39 +315.69 +315.69 +315.69 +315.76 +315.69 +315.69 +315.76 +315.69 +315.69 +315.69 +315.69 
-//END
-//START
-C516C1B813298014352G48-1NN138WXW00236"BV:12.4  Channel:5 Time:3 +295.26 +295.41 +295.33 +295.18 +295.04 +294.96 +294.89 +294.96 +295.04 +295.11 +295.26 +295.41 Channel:6 Time:3 +315.39 +315.39 +315.32 +315.24 +315.10 +315.10 +315.02 +315.02 +315.17 +315.32 +315.46 +315.61 
-//END
-//START
-C516C1B813297134352G47-1NN138WXW00236"BV:12.6  Channel:5 Time:3 +295.48 +295.63 +295.77 +295.85 +295.77 +295.70 +295.85 +295.63 +295.70 +295.63 +295.70 +295.55 Channel:6 Time:3 +315.61 +315.98 +315.98 +315.98 +315.98 +315.98 +315.83 +315.83 +315.98 +315.76 +315.76 +315.83 
-//END
-//START
-C516C1B813297014352G48-1NN138WXW00236"BV:12.4  Channel:5 Time:3 +295.55 +295.48 +295.33 +295.33 +295.26 +295.11 +295.11 +295.18 +295.11 +295.18 +295.33 +295.48 Channel:6 Time:3 +315.54 +315.61 +315.46 +315.46 +315.32 +315.24 +315.17 +315.24 +315.24 +315.39 +315.54 +315.76 
-//END
-//START
-C516C1B813296134352G47-1NN138WXW00236"BV:12.6  Channel:5 Time:3 +295.48 +295.70 +295.77 +295.85 +295.85 +295.77 +295.77 +295.77 +295.77 +295.70 +295.70 +295.70 Channel:6 Time:3 +315.54 +315.98 +315.98 +316.05 +316.05 +316.05 +316.05 +315.91 +316.05 +315.83 +315.83 +315.98 
-//END
-//START
-C516C1B813296014352G48-1NN138WXW00236"BV:12.4  Channel:5 Time:3 +295.33 +295.63 +295.48 +295.33 +295.33 +295.26 +295.18 +295.18 +295.18 +295.26 +295.48 +295.55 Channel:6 Time:3 +315.61 +315.69 +315.69 +315.39 +315.46 +315.46 +315.46 +315.46 +315.46 +315.61 +315.76 +315.83 
-//END
-//START
-C516C1B813295134352G48-1NN138WXW00236"BV:12.6  Channel:5 Time:3 +295.48 +295.63 +295.70 +295.70 +295.85 +295.63 +295.63 +295.63 +295.70 +295.70 +295.55 +295.70 Channel:6 Time:3 +315.69 +316.13 +316.05 +316.20 +316.13 +316.13 +316.05 +316.05 +315.98 +315.91 +315.91 +315.91 
-//END
-//START
-C516C1B813295014352G47-1NN138WXW00236"BV:12.4  Channel:5 Time:3 +295.33 +295.55 +295.63 +295.55 +295.55 +295.48 +295.41 +295.41 +295.48 +295.55 +295.55 +295.70 Channel:6 Time:3 +315.69 +315.76 +315.76 +315.83 +315.76 +315.61 +315.69 +315.61 +315.69 +315.83 +315.91 +315.98 
-//END
-//START
-C516C1B813294134352G47-1NN138WXW00236"BV:12.6  Channel:5 Time:3 +295.55 +296.00 +295.92 +295.85 +295.85 +295.85 +295.85 +296.00 +295.85 +295.77 +295.85 +295.85 Channel:6 Time:3 +315.83 +316.20 +316.13 +316.35 +316.13 +316.13 +316.28 +316.20 +316.20 +316.20 +316.05 +316.20 
-//END
-//START
-C516C1B813294014352G48-1NN138WXW00236"BV:12.4  Channel:5 Time:3 +295.63 +295.77 +295.63 +295.63 +295.55 +295.55 +295.48 +295.48 +295.48 +295.63 +295.63 +295.77 Channel:6 Time:3 +315.83 +315.98 +315.98 +315.91 +315.91 +315.83 +315.83 +315.91 +315.91 +315.91 +315.98 +316.20 
-//END
-//START
-C516C1B813293134353G48-1NN138WXW00236"BV:12.5  Channel:5 Time:3 +295.48 +295.92 +295.85 +295.92 +295.92 +295.85 +295.85 +295.92 +295.77 +295.85 +295.70 +295.63 Channel:6 Time:3 +315.91 +316.20 +316.35 +316.35 +316.42 +316.57 +316.42 +316.35 +316.35 +316.35 +316.28 +316.28 
-//END
-//START
-C516C1B813293014352G48-1NN138WXW00236"BV:12.4  Channel:5 Time:3 +295.48 +295.63 +295.41 +295.41 +295.33 +295.41 +295.48 +295.41 +295.26 +295.41 +295.55 +295.63 Channel:6 Time:3 +315.98 +316.13 +316.13 +315.91 +315.91 +315.91 +315.91 +315.83 +315.98 +315.98 +316.13 +316.28 
-//END
-//START
-C516C1B813292134352G47-1NN138WXW00236"BV:12.6  Channel:5 Time:3 +295.33 +295.70 +295.77 +295.70 +295.77 +295.70 +295.70 +295.70 +295.70 +295.70 +295.63 +295.70 Channel:6 Time:3 +316.05 +316.35 +316.35 +316.35 +316.28 +316.42 +316.42 +316.42 +316.28 +316.13 +316.28 +316.13 
-//END
-//START
-C516C1B813292014352G47-1NN138WXW00236"BV:12.5  Channel:5 Time:3 +295.33 +295.85 +295.70 +295.70 +295.77 +295.63 +295.70 +295.70 +295.77 +295.77 +295.77 +295.70 Channel:6 Time:3 +315.98 +316.13 +316.20 +316.28 +316.20 +316.20 +316.20 +316.28 +316.35 +316.35 +316.42 +316.35 
-//END
-</PRE>
diff --git a/test/noaa_goes_test.py b/test/noaa_goes_test.py
new file mode 100644
index 00000000..6704956d
--- /dev/null
+++ b/test/noaa_goes_test.py
@@ -0,0 +1,322 @@
+from __future__ import print_function
+from datetime import datetime
+import pandas as pd
+import numpy as np
+from pandas.testing import assert_frame_equal
+from ulmo.noaa import goes
+import test_util
+
+message_test_sets = [
+    {
+        'dcp_address': 'C5149430',
+        'parser': 'twdb_stevens',
+        'message_timestamp': "/Date(1559316497303)/",
+    },
+    {
+        'dcp_address': 'C514D73A',
+        'parser': 'twdb_sutron',
+        'message_timestamp': "/Date(1559158095000)/",
+    },
+    {
+        'dcp_address': 'C516C1B8',
+        'parser': 'stevens',
+        'message_timestamp': "/Date(1559569431753)/",
+    }
+]
+
+
+def test_parse_dcp_message_timestamp():
+    for test_set in message_test_sets:
+        dcp_data_file = 'noaa/goes/' + test_set['dcp_address'] + '.txt'
+        with test_util.mocked_urls(dcp_data_file, force=True):
+            data = goes.get_data(test_set['dcp_address'], hours=12)
+            assert data['message_timestamp_utc'][-1] == datetime.fromtimestamp(
+                int(test_set['message_timestamp'].strip('/Date()'))/1000
+            )
+        assert data['message_timestamp_utc'][-1] == datetime.fromtimestamp(
+            int(test_set['message_timestamp'].strip('/Date()'))/1000
+        )
+
+
+twdb_stevens_test_sets = [
+    {
+        'message_timestamp_utc': datetime(2013, 10, 30, 15, 28, 18),
+        'dcp_message': '"BV:11.9  193.76$ 193.70$ 193.62$ 193.54$ 193.49$ 193.43$ 193.37$ 199.62$ 200.51$ 200.98$ 195.00$ 194.33$ ',
+        'dcp_address': '',
+        'return_value': [
+            ['2013-10-30 15:00:00', 'bv', 11.90],
+            ['2013-10-30 15:00:00', 'wl', 193.76],
+            ['2013-10-30 14:00:00', 'wl', 193.70],
+            ['2013-10-30 13:00:00', 'wl', 193.62],
+            ['2013-10-30 12:00:00', 'wl', 193.54],
+            ['2013-10-30 11:00:00', 'wl', 193.49],
+            ['2013-10-30 10:00:00', 'wl', 193.43],
+            ['2013-10-30 09:00:00', 'wl', 193.37],
+            ['2013-10-30 08:00:00', 'wl', 199.62],
+            ['2013-10-30 07:00:00', 'wl', 200.51],
+            ['2013-10-30 06:00:00', 'wl', 200.98],
+            ['2013-10-30 05:00:00', 'wl', 195.00],
+            ['2013-10-30 04:00:00', 'wl', 194.33],
+        ],
+    },
+    {
+        'message_timestamp_utc': datetime(2013, 10, 30, 15, 28, 18),
+        'dcp_message': '"BV:12.6  Channel:5 Time:28 +304.63 +304.63 +304.63 +304.56 +304.63 +304.63 +304.63 +304.63 +304.63 +304.63 +304.63 +304.71 Channel:6 Time:28 +310.51 +310.66 +310.59 +310.51 +310.51 +310.59 +310.59 +310.51 +310.66 +310.51 +310.66 +310.59 ',
+        'dcp_address': '',
+        'return_value': [
+            ['2013-10-30 15:00:00', 'bv', 12.60],
+            ['2013-10-30 15:00:00', 'time', 28.00],
+            ['2013-10-30 15:00:00', 'time', 28.00],
+            ['2013-10-30 15:00:00', '5', 304.63],
+            ['2013-10-30 14:00:00', '5', 304.63],
+            ['2013-10-30 13:00:00', '5', 304.63],
+            ['2013-10-30 12:00:00', '5', 304.56],
+            ['2013-10-30 11:00:00', '5', 304.63],
+            ['2013-10-30 10:00:00', '5', 304.63],
+            ['2013-10-30 09:00:00', '5', 304.63],
+            ['2013-10-30 08:00:00', '5', 304.63],
+            ['2013-10-30 07:00:00', '5', 304.63],
+            ['2013-10-30 06:00:00', '5', 304.63],
+            ['2013-10-30 05:00:00', '5', 304.63],
+            ['2013-10-30 04:00:00', '5', 304.71],
+            ['2013-10-30 15:00:00', '6', 310.51],
+            ['2013-10-30 14:00:00', '6', 310.66],
+            ['2013-10-30 13:00:00', '6', 310.59],
+            ['2013-10-30 12:00:00', '6', 310.51],
+            ['2013-10-30 11:00:00', '6', 310.51],
+            ['2013-10-30 10:00:00', '6', 310.59],
+            ['2013-10-30 09:00:00', '6', 310.59],
+            ['2013-10-30 08:00:00', '6', 310.51],
+            ['2013-10-30 07:00:00', '6', 310.66],
+            ['2013-10-30 06:00:00', '6', 310.51],
+            ['2013-10-30 05:00:00', '6', 310.66],
+            ['2013-10-30 04:00:00', '6', 310.59],
+        ]
+    },
+    {
+        'message_timestamp_utc': datetime(2013, 10, 30, 15, 28, 18),
+        'dcp_message': '"BV:12.6 ',
+        'dcp_address': '',
+        'return_value': [
+            ['2013-10-30 15:00:00', 'bv', 12.60],
+        ]
+    },
+    {
+        'message_timestamp_utc': datetime(2013, 10, 30, 15, 28, 18),
+        'dcp_message': """79."$}X^pZBF8iB~i>>Xmj[bvr^Zv%JXl,DU=l{uu[ time(|@2q^sjS!""",
+        'dcp_address': '',
+        'return_value': pd.DataFrame()
+    },
+]
+
+
+def test_parser_twdb_stevens():
+    for test_set in twdb_stevens_test_sets:
+        print('testing twdb_stevens parser')
+
+        if isinstance(test_set['return_value'], pd.DataFrame):
+            parser = getattr(goes.parsers, 'twdb_stevens')
+            assert_frame_equal(pd.DataFrame(), parser(test_set))
+            return
+        else:
+            columns = ['timestamp_utc', 'channel', 'channel_data']
+        _assert(test_set, columns, 'twdb_stevens')
+
+
+twdb_sutron_test_sets = [
+    {
+        'message_timestamp_utc': datetime(2013, 10, 30, 15, 28, 18),
+        'dcp_message': '":Sense01 60 #60 -67.84 -66.15 -67.73 -67.81 -66.42 -68.45 -68.04 -67.87 -71.53 -73.29 -70.55 -72.71 :BL 13.29',
+        'return_value': [
+            ['2013-10-30 15:00:00', 'sense01', 67.84],
+            ['2013-10-30 14:00:00', 'sense01', 66.15],
+            ['2013-10-30 13:00:00', 'sense01', 67.73],
+            ['2013-10-30 12:00:00', 'sense01', 67.81],
+            ['2013-10-30 11:00:00', 'sense01', 66.42],
+            ['2013-10-30 10:00:00', 'sense01', 68.45],
+            ['2013-10-30 09:00:00', 'sense01', 68.04],
+            ['2013-10-30 08:00:00', 'sense01', 67.87],
+            ['2013-10-30 07:00:00', 'sense01', 71.53],
+            ['2013-10-30 06:00:00', 'sense01', 73.29],
+            ['2013-10-30 05:00:00', 'sense01', 70.55],
+            ['2013-10-30 04:00:00', 'sense01', 72.71],
+            ['2013-10-30 03:00:00', 'sense01', np.nan],
+            ['2013-10-30 15:00:00', 'bl', np.nan],
+            ['2013-10-30 14:00:00', 'bl', 13.29],
+        ],
+    },
+    {
+        'message_timestamp_utc': datetime(2013, 10, 30, 15, 28, 18),
+        'dcp_message': '":OTT 703 60 #60 -231.47 -231.45 -231.44 -231.45 -231.47 -231.50 -231.51 -231.55 -231.56 -231.57 -231.55 -231.53 :6910704 60 #60 -261.85 -261.83 -261.81 -261.80 -261.81 -261.83 -261.85 -261.87 -261.89 -261.88 -261.86 -261.83 :BL 13.21',
+        'return_value': [
+            ['2013-10-30 15:00:00', 'ott', np.nan],
+            ['2013-10-30 14:00:00', 'ott', 231.47],
+            ['2013-10-30 13:00:00', 'ott', 231.45],
+            ['2013-10-30 12:00:00', 'ott', 231.44],
+            ['2013-10-30 11:00:00', 'ott', 231.45],
+            ['2013-10-30 10:00:00', 'ott', 231.47],
+            ['2013-10-30 09:00:00', 'ott', 231.50],
+            ['2013-10-30 08:00:00', 'ott', 231.51],
+            ['2013-10-30 07:00:00', 'ott', 231.55],
+            ['2013-10-30 06:00:00', 'ott', 231.56],
+            ['2013-10-30 05:00:00', 'ott', 231.57],
+            ['2013-10-30 04:00:00', 'ott', 231.55],
+            ['2013-10-30 03:00:00', 'ott', 231.53],
+            ['2013-10-30 02:00:00', 'ott', np.nan],
+            ['2013-10-30 15:00:00', '6910704', 261.85],
+            ['2013-10-30 14:00:00', '6910704', 261.83],
+            ['2013-10-30 13:00:00', '6910704', 261.81],
+            ['2013-10-30 12:00:00', '6910704', 261.80],
+            ['2013-10-30 11:00:00', '6910704', 261.81],
+            ['2013-10-30 10:00:00', '6910704', 261.83],
+            ['2013-10-30 09:00:00', '6910704', 261.85],
+            ['2013-10-30 08:00:00', '6910704', 261.87],
+            ['2013-10-30 07:00:00', '6910704', 261.89],
+            ['2013-10-30 06:00:00', '6910704', 261.88],
+            ['2013-10-30 05:00:00', '6910704', 261.86],
+            ['2013-10-30 04:00:00', '6910704', 261.83],
+            ['2013-10-30 03:00:00', '6910704', np.nan],
+            ['2013-10-30 15:00:00', 'bl', np.nan],
+            ['2013-10-30 14:00:00', 'bl', 13.21],
+        ]
+    },
+]
+
+
+def test_parser_twdb_sutron():
+    for test_set in twdb_sutron_test_sets:
+        print('testing twdb_sutron parser')
+        columns = ['timestamp_utc', 'channel', 'channel_data']
+        _assert(test_set, columns, 'twdb_sutron')
+
+
+twdb_texuni_test_sets = [
+    {
+        'message_timestamp_utc': datetime(2013, 10, 30, 15, 28, 18),
+        'dcp_message': ' \r\n+0.000,-109.8,\r\n+0.000,-109.8,\r\n+0.000,-109.8,\r\n+0.000,-109.8,\r\n+0.000,-109.8,\r\n+0.000,-109.9,\r\n+0.000,-109.9,\r\n+0.000,-109.9,\r\n+0.000,-109.9,\r\n+0.000,-109.9,\r\n+0.000,-110.0,\r\n+0.000,-110.0,\r\n+0.000,-109.9,\r\n+0.000,-109.9,\r\n+0.000,-109.9,\r\n+0.000,-109.9,\r\n+0.000,-110.0,\r\n+0.000,-110.0,\r\n+0.000,-110.0,\r\n+0.000,-110.1,\r\n+0.000,-110.1,\r\n+0.000,-110.1,\r\n+0.000,-110.1,\r\n+0.000,-110.1,\r\n+340.0,+2013.,+307.0,+1400.,+12.07,+0.000,-109.9,-109.8,-110.1,+30.57,',
+        'return_value': [
+            ['2013-10-30 15:00:00', 'wl', 109.8],
+            ['2013-10-30 14:00:00', 'wl', 109.8],
+            ['2013-10-30 13:00:00', 'wl', 109.8],
+            ['2013-10-30 12:00:00', 'wl', 109.8],
+            ['2013-10-30 11:00:00', 'wl', 109.8],
+            ['2013-10-30 10:00:00', 'wl', 109.9],
+            ['2013-10-30 09:00:00', 'wl', 109.9],
+            ['2013-10-30 08:00:00', 'wl', 109.9],
+            ['2013-10-30 07:00:00', 'wl', 109.9],
+            ['2013-10-30 06:00:00', 'wl', 109.9],
+            ['2013-10-30 05:00:00', 'wl', 110.0],
+            ['2013-10-30 04:00:00', 'wl', 110.0],
+            ['2013-10-30 03:00:00', 'wl', 109.9],
+            ['2013-10-30 02:00:00', 'wl', 109.9],
+            ['2013-10-30 01:00:00', 'wl', 109.9],
+            ['2013-10-30 00:00:00', 'wl', 109.9],
+            ['2013-10-29 23:00:00', 'wl', 110.0],
+            ['2013-10-29 22:00:00', 'wl', 110.0],
+            ['2013-10-29 21:00:00', 'wl', 110.0],
+            ['2013-10-29 20:00:00', 'wl', 110.1],
+            ['2013-10-29 19:00:00', 'wl', 110.1],
+            ['2013-10-29 18:00:00', 'wl', 110.1],
+            ['2013-10-29 17:00:00', 'wl', 110.1],
+            ['2013-10-29 16:00:00', 'wl', 110.1],
+        ]
+    },
+]
+
+
+def test_parser_twdb_texuni():
+    for test_set in twdb_texuni_test_sets:
+        print('testing twdb_texuni parser')
+        columns = ['timestamp_utc', 'channel', 'channel_data']
+        _assert(test_set, columns, 'twdb_texuni')
+
+
+twdb_fts_test_sets = [
+    {
+        'message_timestamp_utc': datetime(2018, 2, 6, 13, 36, 14),
+        'dcp_message': ':WL 31 #60 -72.90 -72.88 -72.87 -72.87 -72.87 ' +
+                       '-72.87 -72.88 -72.88 -72.87 -72.87 -72.87 -72.85 ',
+        'return_value': [
+            ['2018-02-06 13:00:00', 'wl', 72.90],
+            ['2018-02-06 12:00:00', 'wl', 72.88],
+            ['2018-02-06 11:00:00', 'wl', 72.87],
+            ['2018-02-06 10:00:00', 'wl', 72.87],
+            ['2018-02-06 09:00:00', 'wl', 72.87],
+            ['2018-02-06 08:00:00', 'wl', 72.87],
+            ['2018-02-06 07:00:00', 'wl', 72.88],
+            ['2018-02-06 06:00:00', 'wl', 72.88],
+            ['2018-02-06 05:00:00', 'wl', 72.87],
+            ['2018-02-06 04:00:00', 'wl', 72.87],
+            ['2018-02-06 03:00:00', 'wl', 72.87],
+            ['2018-02-06 02:00:00', 'wl', 72.85],
+        ]
+    },
+    {
+        'message_timestamp_utc': datetime(2020, 1, 14, 13, 36, 14),
+        'dcp_message': '":vb 119 #72 13.00 :wl 59 #60 -217.66 -217.66 ' +
+                       '-217.64 -217.60 -217.56 -217.51 -217.45 -217.40 ' +
+                       '-217.38 -217.39 -217.41 -217.47',
+        'return_value': [
+            ['2020-01-14 13:00:00', 'vb', 13.0],
+            ['2020-01-14 13:00:00', 'wl', 217.66],
+            ['2020-01-14 12:00:00', 'wl', 217.66],
+            ['2020-01-14 11:00:00', 'wl', 217.64],
+            ['2020-01-14 10:00:00', 'wl', 217.60],
+            ['2020-01-14 09:00:00', 'wl', 217.56],
+            ['2020-01-14 08:00:00', 'wl', 217.51],
+            ['2020-01-14 07:00:00', 'wl', 217.45],
+            ['2020-01-14 06:00:00', 'wl', 217.40],
+            ['2020-01-14 05:00:00', 'wl', 217.38],
+            ['2020-01-14 04:00:00', 'wl', 217.39],
+            ['2020-01-14 03:00:00', 'wl', 217.41],
+            ['2020-01-14 02:00:00', 'wl', 217.47],
+        ]
+    },
+    {
+        'message_timestamp_utc': datetime(2019, 12, 6, 1, 0, 0),
+        'dcp_message': '"Operator Initiated Test Transmission: Operator' +
+                       'Initiated Test Transmission: Operator Initiated Test' +
+                       'Transmission: Operator Initiated Test Transmission: ' +
+                       'Operator Initiated Test Transmission: Operator ' +
+                       'Initiated Test Transmission: Operator Initiated ' +
+                       'Test Transmission: Operator Initiated Test ' +
+                       'Transmission: Operator Initiated Test Transmission: ' +
+                       'Operator Initiated Test Transmission:',
+        'return_value': [
+            ['2019-12-06 01:00:00', np.nan, np.nan],
+        ]
+    },
+]
+
+
+def test_parser_twdb_fts():
+    for test_set in twdb_fts_test_sets:
+        print('testing twdb_fts parser')
+        columns = ['timestamp_utc', 'channel', 'channel_data']
+        _assert(test_set, columns, 'twdb_fts')
+
+
+def _assert(test_set, columns, parser):
+    expected = pd.DataFrame(test_set['return_value'], columns=columns)
+    expected.index = pd.to_datetime(expected['timestamp_utc'])
+    del expected['timestamp_utc']
+    parser = getattr(goes.parsers, parser)
+    df = parser(test_set)
+    print('Expected:')
+    print(expected)
+    print('Actual:')
+    print(df)
+    # to compare pandas dataframes, columns must be in same order
+    if 'channel' in df.columns:
+        for channel in np.unique(df['channel']):
+            df_c = df[df['channel'] == channel]
+            expected_c = expected[expected['channel'] == channel]
+            assert_frame_equal(
+                df_c.sort_index(axis=1).sort_index(axis=0),
+                expected_c.sort_index(axis=1).sort_index(axis=0)
+            )
+    else:
+        assert_frame_equal(
+            df.sort_index(axis=1).sort_index(axis=0),
+            expected.sort_index(axis=1).sort_index(axis=0)
+        )
diff --git a/test/test_util.py b/test/test_util.py
index 27888fa9..ae8a4c1f 100644
--- a/test/test_util.py
+++ b/test/test_util.py
@@ -49,7 +49,7 @@ def mocked_urls(url_files, methods=None, force=False):
     ULMO_DONT_MOCK_TESTS; if that environment variable is set then urls will
     not be mocked and the HTTP requests will go over the network. For example,
     this could be used to to run the whole test suite without mocking files:
-    #    env ULMO_DONT_MOCK_TESTS=1 py.test
+    #    env ulmo_dont_mock_tests=1 py.test
 
     Parameters
     ----------
diff --git a/test/usgs_eddn_test.py b/test/usgs_eddn_test.py
deleted file mode 100644
index 79f61f6e..00000000
--- a/test/usgs_eddn_test.py
+++ /dev/null
@@ -1,335 +0,0 @@
-from __future__ import print_function
-from datetime import datetime
-import pandas as pd
-from pandas.util.testing import assert_frame_equal
-import ulmo
-import ulmo.usgs.eddn.parsers as parsers
-import test_util
-
-fmt = '%y%j%H%M%S'
-
-message_test_sets = [
-    {
-        'dcp_address': 'C5149430',
-        'number_of_lines': 4,
-        'parser': 'twdb_stevens',
-        'first_row_message_timestamp_utc': datetime.strptime('13305152818', fmt),
-    },
-    {
-        'dcp_address': 'C514D73A',
-        'number_of_lines': 4,
-        'parser': 'twdb_sutron',
-        'first_row_message_timestamp_utc': datetime.strptime('13305072816', fmt),
-    },
-    {
-        'dcp_address': 'C516C1B8',
-        'number_of_lines': 28,
-        'parser': 'stevens',
-        'first_row_message_timestamp_utc': datetime.strptime('13305134352', fmt),
-    }
-]
-
-
-def test_parse_dcp_message_number_of_lines():
-    for test_set in message_test_sets:
-        dcp_data_file = 'usgs/eddn/' + test_set['dcp_address'] + '.txt'
-        with test_util.mocked_urls(dcp_data_file, force=True):
-            data = ulmo.usgs.eddn.get_data(test_set['dcp_address'])
-            assert len(data) == test_set['number_of_lines']
-
-
-def test_parse_dcp_message_timestamp():
-    for test_set in message_test_sets:
-        dcp_data_file = 'usgs/eddn/' + test_set['dcp_address'] + '.txt'
-        with test_util.mocked_urls(dcp_data_file, force=True):
-            data = ulmo.usgs.eddn.get_data(test_set['dcp_address'])
-            assert data['message_timestamp_utc'][-1] == test_set['first_row_message_timestamp_utc']
-
-
-multi_message_test_sets = [
-    {
-        'dcp_address': 'C5149430',
-        'data_files': {
-            '.*DRS_UNTIL=now.*':'usgs/eddn/C5149430_file1.txt',
-            '.*DRS_UNTIL=2013%2F294.*':'usgs/eddn/C5149430_file2.txt',
-            '.*DRS_UNTIL=2013%2F207.*':'usgs/eddn/C5149430_file3.txt'
-        },
-        'first_row_message_timestamp_utc': datetime.strptime('14016152818', fmt),
-        'last_row_message_timestamp_utc': datetime.strptime('13202032818', fmt),
-        'number_of_lines': 360,
-        'start': 'P365D'
-    }
-]
-
-
-def test_multi_message_download():
-    for test_set in multi_message_test_sets:
-        with test_util.mocked_urls(test_set['data_files'], force=True):
-            data = ulmo.usgs.eddn.get_data(test_set['dcp_address'], start=test_set['start'])
-            assert data['message_timestamp_utc'][-1] == test_set['first_row_message_timestamp_utc']
-            assert data['message_timestamp_utc'][0] == test_set['last_row_message_timestamp_utc']
-            assert len(data) == test_set['number_of_lines']
-
-
-twdb_stevens_test_sets = [
-    {
-        'message_timestamp_utc': datetime(2013,10,30,15,28,18),
-        'dcp_message': '"BV:11.9  193.76$ 193.70$ 193.62$ 193.54$ 193.49$ 193.43$ 193.37$ 199.62$ 200.51$ 200.98$ 195.00$ 194.33$ ',
-        'return_value': [
-            ['2013-10-30 04:00:00', pd.np.nan, 193.76],
-            ['2013-10-30 05:00:00', pd.np.nan, 193.70],
-            ['2013-10-30 06:00:00', pd.np.nan, 193.62],
-            ['2013-10-30 07:00:00', pd.np.nan, 193.54],
-            ['2013-10-30 08:00:00', pd.np.nan, 193.49],
-            ['2013-10-30 09:00:00', pd.np.nan, 193.43],
-            ['2013-10-30 10:00:00', pd.np.nan, 193.37],
-            ['2013-10-30 11:00:00', pd.np.nan, 199.62],
-            ['2013-10-30 12:00:00', pd.np.nan, 200.51],
-            ['2013-10-30 13:00:00', pd.np.nan, 200.98],
-            ['2013-10-30 14:00:00', pd.np.nan, 195.00],
-            ['2013-10-30 15:00:00', 11.9, 194.33],
-        ],
-    },
-    {
-        'message_timestamp_utc': datetime(2013,10,30,15,28,18),
-        'dcp_message': '"BV:12.6  Channel:5 Time:28 +304.63 +304.63 +304.63 +304.56 +304.63 +304.63 +304.63 +304.63 +304.63 +304.63 +304.63 +304.71 Channel:6 Time:28 +310.51 +310.66 +310.59 +310.51 +310.51 +310.59 +310.59 +310.51 +310.66 +310.51 +310.66 +310.59 ',
-        'return_value': [
-            ['2013-10-30 04:00:00', '5', '28', pd.np.nan, 304.63],
-            ['2013-10-30 05:00:00', '5', '28', pd.np.nan, 304.63],
-            ['2013-10-30 06:00:00', '5', '28', pd.np.nan, 304.63],
-            ['2013-10-30 07:00:00', '5', '28', pd.np.nan, 304.56],
-            ['2013-10-30 08:00:00', '5', '28', pd.np.nan, 304.63],
-            ['2013-10-30 09:00:00', '5', '28', pd.np.nan, 304.63],
-            ['2013-10-30 10:00:00', '5', '28', pd.np.nan, 304.63],
-            ['2013-10-30 11:00:00', '5', '28', pd.np.nan, 304.63],
-            ['2013-10-30 12:00:00', '5', '28', pd.np.nan, 304.63],
-            ['2013-10-30 13:00:00', '5', '28', pd.np.nan, 304.63],
-            ['2013-10-30 14:00:00', '5', '28', pd.np.nan, 304.63],
-            ['2013-10-30 15:00:00', '5', '28', 12.6,      304.71],
-            ['2013-10-30 04:00:00', '6', '28', pd.np.nan, 310.51],
-            ['2013-10-30 05:00:00', '6', '28', pd.np.nan, 310.66],
-            ['2013-10-30 06:00:00', '6', '28', pd.np.nan, 310.59],
-            ['2013-10-30 07:00:00', '6', '28', pd.np.nan, 310.51],
-            ['2013-10-30 08:00:00', '6', '28', pd.np.nan, 310.51],
-            ['2013-10-30 09:00:00', '6', '28', pd.np.nan, 310.59],
-            ['2013-10-30 10:00:00', '6', '28', pd.np.nan, 310.59],
-            ['2013-10-30 11:00:00', '6', '28', pd.np.nan, 310.51],
-            ['2013-10-30 12:00:00', '6', '28', pd.np.nan, 310.66],
-            ['2013-10-30 13:00:00', '6', '28', pd.np.nan, 310.51],
-            ['2013-10-30 14:00:00', '6', '28', pd.np.nan, 310.66],
-            ['2013-10-30 15:00:00', '6', '28', 12.6,      310.59],
-        ]
-    },
-    {
-        'message_timestamp_utc': datetime(2013,10,30,15,28,18),
-        'dcp_message': '"BV:12.6 ',
-        'return_value': pd.DataFrame()
-    },
-        {
-        'message_timestamp_utc': datetime(2013,10,30,15,28,18),
-        'dcp_message': """ 79."$}X^pZBF8iB~i>>Xmj[bvr^Zv%JXl,DU=l{uu[ t(
-|@2q^sjS!
- """,
-        'return_value': pd.DataFrame()
-    },
-]
-
-
-def test_parser_twdb_stevens():
-    for test_set in twdb_stevens_test_sets:
-        print('testing twdb_stevens parser')
-
-        if isinstance(test_set['return_value'], pd.DataFrame):
-            parser = getattr(parsers, 'twdb_stevens')
-            assert_frame_equal(pd.DataFrame(), parser(test_set))
-            return
-
-        if len(test_set['return_value'][0]) == 3:
-            columns = ['timestamp_utc', 'battery_voltage', 'water_level']
-        else:
-            columns = ['timestamp_utc', 'channel', 'time', 'battery_voltage', 'water_level']
-
-        _assert(test_set, columns, 'twdb_stevens')
-
-
-twdb_sutron_test_sets = [
-    {
-        'message_timestamp_utc': datetime(2013,10,30,15,28,18),
-        'dcp_message': '":Sense01 60 #60 -67.84 -66.15 -67.73 -67.81 -66.42 -68.45 -68.04 -67.87 -71.53 -73.29 -70.55 -72.71 :BL 13.29',
-        'return_value': [
-            ['2013-10-30 04:00:00', 'sense01', pd.np.nan, 72.71],
-            ['2013-10-30 05:00:00', 'sense01', pd.np.nan, 70.55],
-            ['2013-10-30 06:00:00', 'sense01', pd.np.nan, 73.29],
-            ['2013-10-30 07:00:00', 'sense01', pd.np.nan, 71.53],
-            ['2013-10-30 08:00:00', 'sense01', pd.np.nan, 67.87],
-            ['2013-10-30 09:00:00', 'sense01', pd.np.nan, 68.04],
-            ['2013-10-30 10:00:00', 'sense01', pd.np.nan, 68.45],
-            ['2013-10-30 11:00:00', 'sense01', pd.np.nan, 66.42],
-            ['2013-10-30 12:00:00', 'sense01', pd.np.nan, 67.81],
-            ['2013-10-30 13:00:00', 'sense01', pd.np.nan, 67.73],
-            ['2013-10-30 14:00:00', 'sense01', pd.np.nan, 66.15],
-            ['2013-10-30 15:00:00', 'sense01', 13.29, 67.84],
-        ],
-    },
-    {
-        'message_timestamp_utc': datetime(2013,10,30,15,28,18),
-        'dcp_message': '":OTT 703 60 #60 -231.47 -231.45 -231.44 -231.45 -231.47 -231.50 -231.51 -231.55 -231.56 -231.57 -231.55 -231.53 :6910704 60 #60 -261.85 -261.83 -261.81 -261.80 -261.81 -261.83 -261.85 -261.87 -261.89 -261.88 -261.86 -261.83 :BL 13.21',
-        'return_value': [
-            ['2013-10-30 04:00:00', 'ott 703', pd.np.nan, 231.53],
-            ['2013-10-30 05:00:00', 'ott 703', pd.np.nan, 231.55],
-            ['2013-10-30 06:00:00', 'ott 703', pd.np.nan, 231.57],
-            ['2013-10-30 07:00:00', 'ott 703', pd.np.nan, 231.56],
-            ['2013-10-30 08:00:00', 'ott 703', pd.np.nan, 231.55],
-            ['2013-10-30 09:00:00', 'ott 703', pd.np.nan, 231.51],
-            ['2013-10-30 10:00:00', 'ott 703', pd.np.nan, 231.50],
-            ['2013-10-30 11:00:00', 'ott 703', pd.np.nan, 231.47],
-            ['2013-10-30 12:00:00', 'ott 703', pd.np.nan, 231.45],
-            ['2013-10-30 13:00:00', 'ott 703', pd.np.nan, 231.44],
-            ['2013-10-30 14:00:00', 'ott 703', pd.np.nan, 231.45],
-            ['2013-10-30 15:00:00', 'ott 703', 13.21, 231.47],
-            ['2013-10-30 04:00:00', '6910704', pd.np.nan, 261.83],
-            ['2013-10-30 05:00:00', '6910704', pd.np.nan, 261.86],
-            ['2013-10-30 06:00:00', '6910704', pd.np.nan, 261.88],
-            ['2013-10-30 07:00:00', '6910704', pd.np.nan, 261.89],
-            ['2013-10-30 08:00:00', '6910704', pd.np.nan, 261.87],
-            ['2013-10-30 09:00:00', '6910704', pd.np.nan, 261.85],
-            ['2013-10-30 10:00:00', '6910704', pd.np.nan, 261.83],
-            ['2013-10-30 11:00:00', '6910704', pd.np.nan, 261.81],
-            ['2013-10-30 12:00:00', '6910704', pd.np.nan, 261.80],
-            ['2013-10-30 13:00:00', '6910704', pd.np.nan, 261.81],
-            ['2013-10-30 14:00:00', '6910704', pd.np.nan, 261.83],
-            ['2013-10-30 15:00:00', '6910704', 13.21, 261.85],
-        ]
-    },
-    {
-        'message_timestamp_utc': datetime(2013,10,30,15,28,18),
-        'dcp_message': '"\r\n// \r\n// \r\n// \r\n// \r\n// \r\n-199.88 \r\n-199.92 \r\n-199.96 \r\n-199.98 \r\n-200.05 \r\n-200.09 \r\n-200.15',
-        'return_value': [
-            ['2013-10-30 04:00:00', pd.np.nan, 200.15],
-            ['2013-10-30 05:00:00', pd.np.nan, 200.09],
-            ['2013-10-30 06:00:00', pd.np.nan, 200.05],
-            ['2013-10-30 07:00:00', pd.np.nan, 199.98],
-            ['2013-10-30 08:00:00', pd.np.nan, 199.96],
-            ['2013-10-30 09:00:00', pd.np.nan, 199.92],
-            ['2013-10-30 10:00:00', pd.np.nan, 199.88],
-            ['2013-10-30 11:00:00', pd.np.nan, pd.np.nan],
-            ['2013-10-30 12:00:00', pd.np.nan, pd.np.nan],
-            ['2013-10-30 13:00:00', pd.np.nan, pd.np.nan],
-            ['2013-10-30 14:00:00', pd.np.nan, pd.np.nan],
-            ['2013-10-30 15:00:00', pd.np.nan, pd.np.nan],
-        ],
-    },
-]
-
-
-def test_parser_twdb_sutron():
-    for test_set in twdb_sutron_test_sets:
-        print('testing twdb_sutron parser')
-        if len(test_set['return_value'][0]) == 3:
-            columns = ['timestamp_utc', 'battery_voltage', 'water_level']
-        else:
-            columns = ['timestamp_utc', 'channel', 'battery_voltage', 'water_level']
-
-        _assert(test_set, columns, 'twdb_sutron')
-
-
-twdb_texuni_test_sets = [
-    {
-        'message_timestamp_utc': datetime(2013,10,30,15,28,18),
-        'dcp_message': ' \r\n+0.000,-109.8,\r\n+0.000,-109.8,\r\n+0.000,-109.8,\r\n+0.000,-109.8,\r\n+0.000,-109.8,\r\n+0.000,-109.9,\r\n+0.000,-109.9,\r\n+0.000,-109.9,\r\n+0.000,-109.9,\r\n+0.000,-109.9,\r\n+0.000,-110.0,\r\n+0.000,-110.0,\r\n+0.000,-109.9,\r\n+0.000,-109.9,\r\n+0.000,-109.9,\r\n+0.000,-109.9,\r\n+0.000,-110.0,\r\n+0.000,-110.0,\r\n+0.000,-110.0,\r\n+0.000,-110.1,\r\n+0.000,-110.1,\r\n+0.000,-110.1,\r\n+0.000,-110.1,\r\n+0.000,-110.1,\r\n+340.0,+2013.,+307.0,+1400.,+12.07,+0.000,-109.9,-109.8,-110.1,+30.57,',
-        'return_value': [
-            ['2013-10-29 16:00:00', pd.np.nan, 109.8],
-            ['2013-10-29 17:00:00', pd.np.nan, 109.8],
-            ['2013-10-29 18:00:00', pd.np.nan, 109.8],
-            ['2013-10-29 19:00:00', pd.np.nan, 109.8],
-            ['2013-10-29 20:00:00', pd.np.nan, 109.8],
-            ['2013-10-29 21:00:00', pd.np.nan, 109.9],
-            ['2013-10-29 22:00:00', pd.np.nan, 109.9],
-            ['2013-10-29 23:00:00', pd.np.nan, 109.9],
-            ['2013-10-30 00:00:00', pd.np.nan, 109.9],
-            ['2013-10-30 01:00:00', pd.np.nan, 109.9],
-            ['2013-10-30 02:00:00', pd.np.nan, 110.0],
-            ['2013-10-30 03:00:00', pd.np.nan, 110.0],
-            ['2013-10-30 04:00:00', pd.np.nan, 109.9],
-            ['2013-10-30 05:00:00', pd.np.nan, 109.9],
-            ['2013-10-30 06:00:00', pd.np.nan, 109.9],
-            ['2013-10-30 07:00:00', pd.np.nan, 109.9],
-            ['2013-10-30 08:00:00', pd.np.nan, 110.0],
-            ['2013-10-30 09:00:00', pd.np.nan, 110.0],
-            ['2013-10-30 10:00:00', pd.np.nan, 110.0],
-            ['2013-10-30 11:00:00', pd.np.nan, 110.1],
-            ['2013-10-30 12:00:00', pd.np.nan, 110.1],
-            ['2013-10-30 13:00:00', pd.np.nan, 110.1],
-            ['2013-10-30 14:00:00', pd.np.nan, 110.1],
-            ['2013-10-30 15:00:00', pd.np.nan, 110.1],
-        ]
-    },
-]
-
-
-def test_parser_twdb_texuni():
-    for test_set in twdb_texuni_test_sets:
-        print('testing twdb_texuni parser')
-        columns = ['timestamp_utc', 'battery_voltage', 'water_level']
-        _assert(test_set, columns, 'twdb_texuni')
-
-twdb_fts_test_sets = [
-    {
-        'message_timestamp_utc': datetime(2018, 2, 6, 13, 36, 14),
-        'dcp_message': ':WL 31 #60 -72.90 -72.88 -72.87 -72.87 -72.87 -72.87 -72.88 -72.88 -72.87 -72.87 -72.87 -72.85 ',
-        'return_value': [
-            ['2018-02-06 13:00:00', pd.np.nan, 72.90],
-            ['2018-02-06 12:00:00', pd.np.nan, 72.88],
-            ['2018-02-06 11:00:00', pd.np.nan, 72.87],
-            ['2018-02-06 10:00:00', pd.np.nan, 72.87],
-            ['2018-02-06 09:00:00', pd.np.nan, 72.87],
-            ['2018-02-06 08:00:00', pd.np.nan, 72.87],
-            ['2018-02-06 07:00:00', pd.np.nan, 72.88],
-            ['2018-02-06 06:00:00', pd.np.nan, 72.88],
-            ['2018-02-06 05:00:00', pd.np.nan, 72.87],
-            ['2018-02-06 04:00:00', pd.np.nan, 72.87],
-            ['2018-02-06 03:00:00', pd.np.nan, 72.87],
-            ['2018-02-06 02:00:00', pd.np.nan, 72.85],
-        ]
-    },
-    {
-        'message_timestamp_utc': datetime(2020, 1, 14, 13, 36, 14),
-        'dcp_message': '":vb 119 #720 13.00 :wl 59 #60 -217.66 -217.66 -217.64 -217.60 -217.56 -217.51 -217.45 -217.40 -217.38 -217.39 -217.41 -217.47',
-        'return_value': [
-            ['2020-01-14 13:00:00', 13.0, 217.66],
-            ['2020-01-14 12:00:00', pd.np.nan, 217.66],
-            ['2020-01-14 11:00:00', pd.np.nan, 217.64],
-            ['2020-01-14 10:00:00', pd.np.nan, 217.60],
-            ['2020-01-14 09:00:00', pd.np.nan, 217.56],
-            ['2020-01-14 08:00:00', pd.np.nan, 217.51],
-            ['2020-01-14 07:00:00', pd.np.nan, 217.45],
-            ['2020-01-14 06:00:00', pd.np.nan, 217.40],
-            ['2020-01-14 05:00:00', pd.np.nan, 217.38],
-            ['2020-01-14 04:00:00', pd.np.nan, 217.39],
-            ['2020-01-14 03:00:00', pd.np.nan, 217.41],
-            ['2020-01-14 02:00:00', pd.np.nan, 217.47],
-        ]
-    },
-]
-
-def test_parser_twdb_fts():
-    for test_set in twdb_fts_test_sets:
-        print('testing twdb_fts parser')
-        columns = ['timestamp_utc', 'battery_voltage', 'water_level']
-        _assert(test_set, columns, 'twdb_fts')
-
-
-def _assert(test_set, columns, parser):
-    expected = pd.DataFrame(test_set['return_value'], columns=columns)
-    expected.index = pd.to_datetime(expected['timestamp_utc'])
-    del expected['timestamp_utc']
-    parser = getattr(parsers, parser)
-    df = parser(test_set)
-    # to compare pandas dataframes, columns must be in same order
-    if 'channel' in df.columns:
-        for channel in pd.np.unique(df['channel']):
-            df_c = df[df['channel']==channel]
-            expected_c = expected[expected['channel']==channel]
-            assert_frame_equal(df_c.sort_index(axis=1).sort_index(axis=0), expected_c.sort_index(axis=1).sort_index(axis=0))
-    else:
-        assert_frame_equal(df.sort_index(axis=1).sort_index(axis=0), expected.sort_index(axis=1).sort_index(axis=0))