diff --git a/.gitignore b/.gitignore index ba74660506d9..f8c2d10e0973 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,9 @@ __pycache__/ *.py[cod] +# ipython extras +.ipynb_checkpoints/ + # C extensions *.so diff --git a/.sublimelinterrc b/.sublimelinterrc new file mode 100644 index 000000000000..82b94db059c1 --- /dev/null +++ b/.sublimelinterrc @@ -0,0 +1,8 @@ +{ + "@python": 3.3, + "linters": { + "pep257": { + "excludes": ["*/test_*.py"] + } + } +} \ No newline at end of file diff --git a/Qcodes example.ipynb b/Qcodes example.ipynb new file mode 100644 index 000000000000..b457e5b98299 --- /dev/null +++ b/Qcodes example.ipynb @@ -0,0 +1,2021 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/site-packages/IPython/kernel/__init__.py:13: ShimWarning: The `IPython.kernel` package has been deprecated. You should import from ipykernel or jupyter_client instead.\n", + " \"You should import from ipykernel or jupyter_client instead.\", ShimWarning)\n" + ] + } + ], + "source": [ + "%matplotlib nbagg\n", + "import matplotlib.pyplot as plt\n", + "\n", + "# load the qcodes path, until we have this installed as a package\n", + "import sys\n", + "\n", + "qcpath = '/Users/alex/qdev/Qcodes'\n", + "if qcpath not in sys.path:\n", + " sys.path.append(qcpath)\n", + "\n", + "# force windows multiprocessing behavior on mac\n", + "import multiprocessing as mp\n", + "mp.set_start_method('spawn')" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[]" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# spawn doesn't like function or class definitions in the interpreter\n", + "# session - had to move them to a file.\n", + "from toymodel import AModel, MockGates, MockSource, MockMeter\n", + "\n", + "# no extra processes running yet\n", + "mp.active_children()" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[]" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# now create this \"experiment\"\n", + "model = AModel()\n", + "gates = MockGates('gates', model)\n", + "source = MockSource('source', model)\n", + "meter = MockMeter('meter', model)\n", + "\n", + "from qcodes.station import Station\n", + "station = Station(gates, source, meter)\n", + "\n", + "# could measure any number of things by adding to this list,\n", + "# but here we're just measuring one, the meter amplitude\n", + "# we can also create multiple measurement sets side-by-side,\n", + "# but only one is the default one stored in the station\n", + "measurement = station.set_measurement(meter['amplitude'])\n", + "\n", + "# it's nice to have the key parameters be part of the global namespace\n", + "# that way they're objects that we can easily set, get, and slice\n", + "# this could be simplified to a station method that gathers all parameters\n", + "# and adds them all as (disambiguated) globals, printing what it did\n", + "# something like:\n", + "# station.gather_parameters(globals())\n", + "c0, c1, c2, vsd = gates['chan0'], gates['chan1'], gates['chan2'], source['amplitude']\n", + "\n", + "# check that a StorageServer is running\n", + "mp.active_children()" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(0.117,)" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# we can get the measured quantities right now\n", + "measurement.get()" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# start a sweep (which by default runs in a seprarate process)\n", + "# the sweep values are defined by slicing the parameter object\n", + "# but more complicated sweeps (eg nonlinear, or adaptive) can\n", + "# easily be used instead\n", + "swp = measurement.sweep(c0[-20:20:0.1], 0.2, location='testsweep')\n", + "swp" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[, ]" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# now there should be two processes running, StorageServer and a sweep\n", + "mp.active_children()" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{'amplitude': array([ 0.117, 0.117, 0.115, 0.111, 0.106, 0.099, 0.092, 0.085,\n", + " 0.077, 0.071, 0.064, 0.058, 0.053, 0.048, 0.044, 0.04 ,\n", + " 0.037, 0.034, 0.031, 0.029, 0.027, 0.025, 0.023, 0.022,\n", + " 0.02 , 0.019, 0.018, 0.017, 0.016, 0.015, 0.014, 0.013,\n", + " 0.013, 0.012, 0.011, 0.011, 0.01 , 0.01 , 0.01 , 0.009,\n", + " 0.009, 0.008, 0.008, 0.008, 0.007, 0.007, 0.007, 0.007,\n", + " 0.006, 0.006, 0.006, 0.006, 0.006, 0.007, 0.007, 0.007,\n", + " 0.007, 0.008, 0.008, 0.008, 0.009, 0.009, 0.01 , 0.01 ,\n", + " 0.01 , 0.011, 0.011, 0.012, 0.013, 0.013, 0.014, 0.015,\n", + " 0.016, 0.017, 0.018, 0.019, 0.02 , 0.022, 0.023, 0.025,\n", + " 0.027, 0.029, 0.031, 0.034, 0.037, 0.04 , 0.044, 0.048,\n", + " 0.053, 0.058, 0.064, 0.071, 0.077, 0.085, 0.092, 0.099,\n", + " 0.106, 0.111, 0.115, 0.117, 0.117, 0.117, 0.115, 0.111,\n", + " 0.106, 0.099, 0.092, 0.085, 0.077, 0.071, 0.064, 0.058,\n", + " 0.053, 0.048, 0.044, 0.04 , 0.037, 0.034, 0.031, 0.029,\n", + " 0.027, 0.025, 0.023, 0.022, 0.02 , 0.019, 0.018, 0.017,\n", + " 0.016, 0.015, 0.014, 0.013, 0.013, 0.012, 0.011, 0.011,\n", + " 0.01 , 0.01 , 0.01 , 0.009, 0.009, 0.008, 0.008, 0.008,\n", + " 0.007, 0.007, 0.007, 0.007, 0.006, 0.006, 0.006, 0.006,\n", + " 0.006, 0.007, 0.007, 0.007, 0.007, 0.008, 0.008, 0.008,\n", + " 0.009, 0.009, 0.01 , 0.01 , 0.01 , 0.011, 0.011, 0.012,\n", + " 0.013, 0.013, 0.014, 0.015, 0.016, 0.017, 0.018, 0.019,\n", + " 0.02 , 0.022, 0.023, 0.025, 0.027, 0.029, 0.031, 0.034,\n", + " 0.037, 0.04 , 0.044, 0.048, 0.053, 0.058, 0.064, 0.071,\n", + " 0.077, 0.085, 0.092, 0.099, 0.106, 0.111, 0.115, 0.117,\n", + " 0.117, 0.117, 0.115, 0.111, 0.106, 0.099, 0.092, 0.085,\n", + " 0.077, 0.071, 0.064, 0.058, 0.053, 0.048, 0.044, 0.04 ,\n", + " 0.037, 0.034, 0.031, 0.029, 0.027, 0.025, 0.023, 0.022,\n", + " 0.02 , 0.019, 0.018, 0.017, 0.016, 0.015, 0.014, 0.013,\n", + " 0.013, 0.012, 0.011, 0.011, 0.01 , 0.01 , 0.01 , 0.009,\n", + " 0.009, 0.008, nan, nan, nan, nan, nan, nan,\n", + " nan, nan, nan, nan, nan, nan, nan, nan,\n", + " nan, nan, nan, nan, nan, nan, nan, nan,\n", + " nan, nan, nan, nan, nan, nan, nan, nan,\n", + " nan, nan, nan, nan, nan, nan, nan, nan,\n", + " nan, nan, nan, nan, nan, nan, nan, nan,\n", + " nan, nan, nan, nan, nan, nan, nan, nan,\n", + " nan, nan, nan, nan, nan, nan, nan, nan,\n", + " nan, nan, nan, nan, nan, nan, nan, nan,\n", + " nan, nan, nan, nan, nan, nan, nan, nan,\n", + " nan, nan, nan, nan, nan, nan, nan, nan,\n", + " nan, nan, nan, nan, nan, nan, nan, nan,\n", + " nan, nan, nan, nan, nan, nan, nan, nan,\n", + " nan, nan, nan, nan, nan, nan, nan, nan,\n", + " nan, nan, nan, nan, nan, nan, nan, nan,\n", + " nan, nan, nan, nan, nan, nan, nan, nan,\n", + " nan, nan, nan, nan, nan, nan, nan, nan,\n", + " nan, nan, nan, nan, nan, nan, nan, nan,\n", + " nan, nan, nan, nan, nan, nan, nan, nan,\n", + " nan, nan, nan, nan, nan, nan, nan, nan]),\n", + " 'chan0': array([-20. , -19.9, -19.8, -19.7, -19.6, -19.5, -19.4, -19.3, -19.2,\n", + " -19.1, -19. , -18.9, -18.8, -18.7, -18.6, -18.5, -18.4, -18.3,\n", + " -18.2, -18.1, -18. , -17.9, -17.8, -17.7, -17.6, -17.5, -17.4,\n", + " -17.3, -17.2, -17.1, -17. , -16.9, -16.8, -16.7, -16.6, -16.5,\n", + " -16.4, -16.3, -16.2, -16.1, -16. , -15.9, -15.8, -15.7, -15.6,\n", + " -15.5, -15.4, -15.3, -15.2, -15.1, -15. , -14.9, -14.8, -14.7,\n", + " -14.6, -14.5, -14.4, -14.3, -14.2, -14.1, -14. , -13.9, -13.8,\n", + " -13.7, -13.6, -13.5, -13.4, -13.3, -13.2, -13.1, -13. , -12.9,\n", + " -12.8, -12.7, -12.6, -12.5, -12.4, -12.3, -12.2, -12.1, -12. ,\n", + " -11.9, -11.8, -11.7, -11.6, -11.5, -11.4, -11.3, -11.2, -11.1,\n", + " -11. , -10.9, -10.8, -10.7, -10.6, -10.5, -10.4, -10.3, -10.2,\n", + " -10.1, -10. , -9.9, -9.8, -9.7, -9.6, -9.5, -9.4, -9.3,\n", + " -9.2, -9.1, -9. , -8.9, -8.8, -8.7, -8.6, -8.5, -8.4,\n", + " -8.3, -8.2, -8.1, -8. , -7.9, -7.8, -7.7, -7.6, -7.5,\n", + " -7.4, -7.3, -7.2, -7.1, -7. , -6.9, -6.8, -6.7, -6.6,\n", + " -6.5, -6.4, -6.3, -6.2, -6.1, -6. , -5.9, -5.8, -5.7,\n", + " -5.6, -5.5, -5.4, -5.3, -5.2, -5.1, -5. , -4.9, -4.8,\n", + " -4.7, -4.6, -4.5, -4.4, -4.3, -4.2, -4.1, -4. , -3.9,\n", + " -3.8, -3.7, -3.6, -3.5, -3.4, -3.3, -3.2, -3.1, -3. ,\n", + " -2.9, -2.8, -2.7, -2.6, -2.5, -2.4, -2.3, -2.2, -2.1,\n", + " -2. , -1.9, -1.8, -1.7, -1.6, -1.5, -1.4, -1.3, -1.2,\n", + " -1.1, -1. , -0.9, -0.8, -0.7, -0.6, -0.5, -0.4, -0.3,\n", + " -0.2, -0.1, 0. , 0.1, 0.2, 0.3, 0.4, 0.5, 0.6,\n", + " 0.7, 0.8, 0.9, 1. , 1.1, 1.2, 1.3, 1.4, 1.5,\n", + " 1.6, 1.7, 1.8, 1.9, 2. , 2.1, 2.2, 2.3, 2.4,\n", + " 2.5, 2.6, 2.7, 2.8, 2.9, 3. , 3.1, 3.2, 3.3,\n", + " 3.4, 3.5, 3.6, 3.7, 3.8, 3.9, 4. , 4.1, nan,\n", + " nan, nan, nan, nan, nan, nan, nan, nan, nan,\n", + " nan, nan, nan, nan, nan, nan, nan, nan, nan,\n", + " nan, nan, nan, nan, nan, nan, nan, nan, nan,\n", + " nan, nan, nan, nan, nan, nan, nan, nan, nan,\n", + " nan, nan, nan, nan, nan, nan, nan, nan, nan,\n", + " nan, nan, nan, nan, nan, nan, nan, nan, nan,\n", + " nan, nan, nan, nan, nan, nan, nan, nan, nan,\n", + " nan, nan, nan, nan, nan, nan, nan, nan, nan,\n", + " nan, nan, nan, nan, nan, nan, nan, nan, nan,\n", + " nan, nan, nan, nan, nan, nan, nan, nan, nan,\n", + " nan, nan, nan, nan, nan, nan, nan, nan, nan,\n", + " nan, nan, nan, nan, nan, nan, nan, nan, nan,\n", + " nan, nan, nan, nan, nan, nan, nan, nan, nan,\n", + " nan, nan, nan, nan, nan, nan, nan, nan, nan,\n", + " nan, nan, nan, nan, nan, nan, nan, nan, nan,\n", + " nan, nan, nan, nan, nan, nan, nan, nan, nan,\n", + " nan, nan, nan, nan, nan, nan, nan, nan, nan,\n", + " nan, nan, nan, nan]),\n", + " 'ts': array([ 1.44503691e+09, 1.44503691e+09, 1.44503691e+09,\n", + " 1.44503691e+09, 1.44503691e+09, 1.44503691e+09,\n", + " 1.44503691e+09, 1.44503691e+09, 1.44503691e+09,\n", + " 1.44503691e+09, 1.44503691e+09, 1.44503691e+09,\n", + " 1.44503691e+09, 1.44503691e+09, 1.44503691e+09,\n", + " 1.44503691e+09, 1.44503691e+09, 1.44503691e+09,\n", + " 1.44503691e+09, 1.44503691e+09, 1.44503691e+09,\n", + " 1.44503691e+09, 1.44503691e+09, 1.44503691e+09,\n", + " 1.44503691e+09, 1.44503691e+09, 1.44503691e+09,\n", + " 1.44503691e+09, 1.44503691e+09, 1.44503691e+09,\n", + " 1.44503691e+09, 1.44503691e+09, 1.44503691e+09,\n", + " 1.44503691e+09, 1.44503691e+09, 1.44503691e+09,\n", + " 1.44503691e+09, 1.44503691e+09, 1.44503691e+09,\n", + " 1.44503691e+09, 1.44503691e+09, 1.44503691e+09,\n", + " 1.44503691e+09, 1.44503691e+09, 1.44503691e+09,\n", + " 1.44503691e+09, 1.44503691e+09, 1.44503691e+09,\n", + " 1.44503691e+09, 1.44503692e+09, 1.44503692e+09,\n", + " 1.44503692e+09, 1.44503692e+09, 1.44503692e+09,\n", + " 1.44503692e+09, 1.44503692e+09, 1.44503692e+09,\n", + " 1.44503692e+09, 1.44503692e+09, 1.44503692e+09,\n", + " 1.44503692e+09, 1.44503692e+09, 1.44503692e+09,\n", + " 1.44503692e+09, 1.44503692e+09, 1.44503692e+09,\n", + " 1.44503692e+09, 1.44503692e+09, 1.44503692e+09,\n", + " 1.44503692e+09, 1.44503692e+09, 1.44503692e+09,\n", + " 1.44503692e+09, 1.44503692e+09, 1.44503692e+09,\n", + " 1.44503692e+09, 1.44503692e+09, 1.44503692e+09,\n", + " 1.44503692e+09, 1.44503692e+09, 1.44503692e+09,\n", + " 1.44503692e+09, 1.44503692e+09, 1.44503692e+09,\n", + " 1.44503692e+09, 1.44503692e+09, 1.44503692e+09,\n", + " 1.44503692e+09, 1.44503692e+09, 1.44503692e+09,\n", + " 1.44503692e+09, 1.44503692e+09, 1.44503692e+09,\n", + " 1.44503692e+09, 1.44503692e+09, 1.44503692e+09,\n", + " 1.44503692e+09, 1.44503692e+09, 1.44503693e+09,\n", + " 1.44503693e+09, 1.44503693e+09, 1.44503693e+09,\n", + " 1.44503693e+09, 1.44503693e+09, 1.44503693e+09,\n", + " 1.44503693e+09, 1.44503693e+09, 1.44503693e+09,\n", + " 1.44503693e+09, 1.44503693e+09, 1.44503693e+09,\n", + " 1.44503693e+09, 1.44503693e+09, 1.44503693e+09,\n", + " 1.44503693e+09, 1.44503693e+09, 1.44503693e+09,\n", + " 1.44503693e+09, 1.44503693e+09, 1.44503693e+09,\n", + " 1.44503693e+09, 1.44503693e+09, 1.44503693e+09,\n", + " 1.44503693e+09, 1.44503693e+09, 1.44503693e+09,\n", + " 1.44503693e+09, 1.44503693e+09, 1.44503693e+09,\n", + " 1.44503693e+09, 1.44503693e+09, 1.44503693e+09,\n", + " 1.44503693e+09, 1.44503693e+09, 1.44503693e+09,\n", + " 1.44503693e+09, 1.44503693e+09, 1.44503693e+09,\n", + " 1.44503693e+09, 1.44503693e+09, 1.44503693e+09,\n", + " 1.44503693e+09, 1.44503693e+09, 1.44503693e+09,\n", + " 1.44503693e+09, 1.44503693e+09, 1.44503693e+09,\n", + " 1.44503694e+09, 1.44503694e+09, 1.44503694e+09,\n", + " 1.44503694e+09, 1.44503694e+09, 1.44503694e+09,\n", + " 1.44503694e+09, 1.44503694e+09, 1.44503694e+09,\n", + " 1.44503694e+09, 1.44503694e+09, 1.44503694e+09,\n", + " 1.44503694e+09, 1.44503694e+09, 1.44503694e+09,\n", + " 1.44503694e+09, 1.44503694e+09, 1.44503694e+09,\n", + " 1.44503694e+09, 1.44503694e+09, 1.44503694e+09,\n", + " 1.44503694e+09, 1.44503694e+09, 1.44503694e+09,\n", + " 1.44503694e+09, 1.44503694e+09, 1.44503694e+09,\n", + " 1.44503694e+09, 1.44503694e+09, 1.44503694e+09,\n", + " 1.44503694e+09, 1.44503694e+09, 1.44503694e+09,\n", + " 1.44503694e+09, 1.44503694e+09, 1.44503694e+09,\n", + " 1.44503694e+09, 1.44503694e+09, 1.44503694e+09,\n", + " 1.44503694e+09, 1.44503694e+09, 1.44503694e+09,\n", + " 1.44503694e+09, 1.44503694e+09, 1.44503694e+09,\n", + " 1.44503694e+09, 1.44503694e+09, 1.44503694e+09,\n", + " 1.44503694e+09, 1.44503695e+09, 1.44503695e+09,\n", + " 1.44503695e+09, 1.44503695e+09, 1.44503695e+09,\n", + " 1.44503695e+09, 1.44503695e+09, 1.44503695e+09,\n", + " 1.44503695e+09, 1.44503695e+09, 1.44503695e+09,\n", + " 1.44503695e+09, 1.44503695e+09, 1.44503695e+09,\n", + " 1.44503695e+09, 1.44503695e+09, 1.44503695e+09,\n", + " 1.44503695e+09, 1.44503695e+09, 1.44503695e+09,\n", + " 1.44503695e+09, 1.44503695e+09, 1.44503695e+09,\n", + " 1.44503695e+09, 1.44503695e+09, 1.44503695e+09,\n", + " 1.44503695e+09, 1.44503695e+09, 1.44503695e+09,\n", + " 1.44503695e+09, 1.44503695e+09, 1.44503695e+09,\n", + " 1.44503695e+09, 1.44503695e+09, 1.44503695e+09,\n", + " 1.44503695e+09, 1.44503695e+09, 1.44503695e+09,\n", + " 1.44503695e+09, 1.44503695e+09, 1.44503695e+09,\n", + " 1.44503695e+09, 1.44503695e+09, 1.44503695e+09,\n", + " 1.44503695e+09, 1.44503695e+09, nan,\n", + " nan, nan, nan,\n", + " nan, nan, nan,\n", + " nan, nan, nan,\n", + " nan, nan, nan,\n", + " nan, nan, nan,\n", + " nan, nan, nan,\n", + " nan, nan, nan,\n", + " nan, nan, nan,\n", + " nan, nan, nan,\n", + " nan, nan, nan,\n", + " nan, nan, nan,\n", + " nan, nan, nan,\n", + " nan, nan, nan,\n", + " nan, nan, nan,\n", + " nan, nan, nan,\n", + " nan, nan, nan,\n", + " nan, nan, nan,\n", + " nan, nan, nan,\n", + " nan, nan, nan,\n", + " nan, nan, nan,\n", + " nan, nan, nan,\n", + " nan, nan, nan,\n", + " nan, nan, nan,\n", + " nan, nan, nan,\n", + " nan, nan, nan,\n", + " nan, nan, nan,\n", + " nan, nan, nan,\n", + " nan, nan, nan,\n", + " nan, nan, nan,\n", + " nan, nan, nan,\n", + " nan, nan, nan,\n", + " nan, nan, nan,\n", + " nan, nan, nan,\n", + " nan, nan, nan,\n", + " nan, nan, nan,\n", + " nan, nan, nan,\n", + " nan, nan, nan,\n", + " nan, nan, nan,\n", + " nan, nan, nan,\n", + " nan, nan, nan,\n", + " nan, nan, nan,\n", + " nan, nan, nan,\n", + " nan, nan, nan,\n", + " nan, nan, nan,\n", + " nan, nan, nan,\n", + " nan, nan, nan,\n", + " nan, nan, nan,\n", + " nan, nan, nan,\n", + " nan, nan, nan,\n", + " nan, nan, nan,\n", + " nan, nan, nan,\n", + " nan, nan, nan,\n", + " nan])}" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# bring the sweep data into the main process and display it as numbers\n", + "swp.sync_live()\n", + "# swp.read()\n", + "swp.data" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": { + "collapsed": false, + "scrolled": false + }, + "outputs": [ + { + "data": { + "application/javascript": [ + "/* Put everything inside the global mpl namespace */\n", + "window.mpl = {};\n", + "\n", + "mpl.get_websocket_type = function() {\n", + " if (typeof(WebSocket) !== 'undefined') {\n", + " return WebSocket;\n", + " } else if (typeof(MozWebSocket) !== 'undefined') {\n", + " return MozWebSocket;\n", + " } else {\n", + " alert('Your browser does not have WebSocket support.' +\n", + " 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n", + " 'Firefox 4 and 5 are also supported but you ' +\n", + " 'have to enable WebSockets in about:config.');\n", + " };\n", + "}\n", + "\n", + "mpl.figure = function(figure_id, websocket, ondownload, parent_element) {\n", + " this.id = figure_id;\n", + "\n", + " this.ws = websocket;\n", + "\n", + " this.supports_binary = (this.ws.binaryType != undefined);\n", + "\n", + " if (!this.supports_binary) {\n", + " var warnings = document.getElementById(\"mpl-warnings\");\n", + " if (warnings) {\n", + " warnings.style.display = 'block';\n", + " warnings.textContent = (\n", + " \"This browser does not support binary websocket messages. \" +\n", + " \"Performance may be slow.\");\n", + " }\n", + " }\n", + "\n", + " this.imageObj = new Image();\n", + "\n", + " this.context = undefined;\n", + " this.message = undefined;\n", + " this.canvas = undefined;\n", + " this.rubberband_canvas = undefined;\n", + " this.rubberband_context = undefined;\n", + " this.format_dropdown = undefined;\n", + "\n", + " this.image_mode = 'full';\n", + "\n", + " this.root = $('
');\n", + " this._root_extra_style(this.root)\n", + " this.root.attr('style', 'display: inline-block');\n", + "\n", + " $(parent_element).append(this.root);\n", + "\n", + " this._init_header(this);\n", + " this._init_canvas(this);\n", + " this._init_toolbar(this);\n", + "\n", + " var fig = this;\n", + "\n", + " this.waiting = false;\n", + "\n", + " this.ws.onopen = function () {\n", + " fig.send_message(\"supports_binary\", {value: fig.supports_binary});\n", + " fig.send_message(\"send_image_mode\", {});\n", + " fig.send_message(\"refresh\", {});\n", + " }\n", + "\n", + " this.imageObj.onload = function() {\n", + " if (fig.image_mode == 'full') {\n", + " // Full images could contain transparency (where diff images\n", + " // almost always do), so we need to clear the canvas so that\n", + " // there is no ghosting.\n", + " fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n", + " }\n", + " fig.context.drawImage(fig.imageObj, 0, 0);\n", + " fig.waiting = false;\n", + " };\n", + "\n", + " this.imageObj.onunload = function() {\n", + " this.ws.close();\n", + " }\n", + "\n", + " this.ws.onmessage = this._make_on_message_function(this);\n", + "\n", + " this.ondownload = ondownload;\n", + "}\n", + "\n", + "mpl.figure.prototype._init_header = function() {\n", + " var titlebar = $(\n", + " '
');\n", + " var titletext = $(\n", + " '
');\n", + " titlebar.append(titletext)\n", + " this.root.append(titlebar);\n", + " this.header = titletext[0];\n", + "}\n", + "\n", + "\n", + "\n", + "mpl.figure.prototype._canvas_extra_style = function(canvas_div) {\n", + "\n", + "}\n", + "\n", + "\n", + "mpl.figure.prototype._root_extra_style = function(canvas_div) {\n", + "\n", + "}\n", + "\n", + "mpl.figure.prototype._init_canvas = function() {\n", + " var fig = this;\n", + "\n", + " var canvas_div = $('
');\n", + "\n", + " canvas_div.attr('style', 'position: relative; clear: both; outline: 0');\n", + "\n", + " function canvas_keyboard_event(event) {\n", + " return fig.key_event(event, event['data']);\n", + " }\n", + "\n", + " canvas_div.keydown('key_press', canvas_keyboard_event);\n", + " canvas_div.keyup('key_release', canvas_keyboard_event);\n", + " this.canvas_div = canvas_div\n", + " this._canvas_extra_style(canvas_div)\n", + " this.root.append(canvas_div);\n", + "\n", + " var canvas = $('');\n", + " canvas.addClass('mpl-canvas');\n", + " canvas.attr('style', \"left: 0; top: 0; z-index: 0; outline: 0\")\n", + "\n", + " this.canvas = canvas[0];\n", + " this.context = canvas[0].getContext(\"2d\");\n", + "\n", + " var rubberband = $('');\n", + " rubberband.attr('style', \"position: absolute; left: 0; top: 0; z-index: 1;\")\n", + "\n", + " var pass_mouse_events = true;\n", + "\n", + " canvas_div.resizable({\n", + " start: function(event, ui) {\n", + " pass_mouse_events = false;\n", + " },\n", + " resize: function(event, ui) {\n", + " fig.request_resize(ui.size.width, ui.size.height);\n", + " },\n", + " stop: function(event, ui) {\n", + " pass_mouse_events = true;\n", + " fig.request_resize(ui.size.width, ui.size.height);\n", + " },\n", + " });\n", + "\n", + " function mouse_event_fn(event) {\n", + " if (pass_mouse_events)\n", + " return fig.mouse_event(event, event['data']);\n", + " }\n", + "\n", + " rubberband.mousedown('button_press', mouse_event_fn);\n", + " rubberband.mouseup('button_release', mouse_event_fn);\n", + " // Throttle sequential mouse events to 1 every 20ms.\n", + " rubberband.mousemove('motion_notify', mouse_event_fn);\n", + "\n", + " rubberband.mouseenter('figure_enter', mouse_event_fn);\n", + " rubberband.mouseleave('figure_leave', mouse_event_fn);\n", + "\n", + " canvas_div.on(\"wheel\", function (event) {\n", + " event = event.originalEvent;\n", + " event['data'] = 'scroll'\n", + " if (event.deltaY < 0) {\n", + " event.step = 1;\n", + " } else {\n", + " event.step = -1;\n", + " }\n", + " mouse_event_fn(event);\n", + " });\n", + "\n", + " canvas_div.append(canvas);\n", + " canvas_div.append(rubberband);\n", + "\n", + " this.rubberband = rubberband;\n", + " this.rubberband_canvas = rubberband[0];\n", + " this.rubberband_context = rubberband[0].getContext(\"2d\");\n", + " this.rubberband_context.strokeStyle = \"#000000\";\n", + "\n", + " this._resize_canvas = function(width, height) {\n", + " // Keep the size of the canvas, canvas container, and rubber band\n", + " // canvas in synch.\n", + " canvas_div.css('width', width)\n", + " canvas_div.css('height', height)\n", + "\n", + " canvas.attr('width', width);\n", + " canvas.attr('height', height);\n", + "\n", + " rubberband.attr('width', width);\n", + " rubberband.attr('height', height);\n", + " }\n", + "\n", + " // Set the figure to an initial 600x600px, this will subsequently be updated\n", + " // upon first draw.\n", + " this._resize_canvas(600, 600);\n", + "\n", + " // Disable right mouse context menu.\n", + " $(this.rubberband_canvas).bind(\"contextmenu\",function(e){\n", + " return false;\n", + " });\n", + "\n", + " function set_focus () {\n", + " canvas.focus();\n", + " canvas_div.focus();\n", + " }\n", + "\n", + " window.setTimeout(set_focus, 100);\n", + "}\n", + "\n", + "mpl.figure.prototype._init_toolbar = function() {\n", + " var fig = this;\n", + "\n", + " var nav_element = $('
')\n", + " nav_element.attr('style', 'width: 100%');\n", + " this.root.append(nav_element);\n", + "\n", + " // Define a callback function for later on.\n", + " function toolbar_event(event) {\n", + " return fig.toolbar_button_onclick(event['data']);\n", + " }\n", + " function toolbar_mouse_event(event) {\n", + " return fig.toolbar_button_onmouseover(event['data']);\n", + " }\n", + "\n", + " for(var toolbar_ind in mpl.toolbar_items) {\n", + " var name = mpl.toolbar_items[toolbar_ind][0];\n", + " var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", + " var image = mpl.toolbar_items[toolbar_ind][2];\n", + " var method_name = mpl.toolbar_items[toolbar_ind][3];\n", + "\n", + " if (!name) {\n", + " // put a spacer in here.\n", + " continue;\n", + " }\n", + " var button = $('');\n", + " button.click(method_name, toolbar_event);\n", + " button.mouseover(tooltip, toolbar_mouse_event);\n", + " nav_element.append(button);\n", + " }\n", + "\n", + " // Add the status bar.\n", + " var status_bar = $('');\n", + " nav_element.append(status_bar);\n", + " this.message = status_bar[0];\n", + "\n", + " // Add the close button to the window.\n", + " var buttongrp = $('
');\n", + " var button = $('');\n", + " button.click(function (evt) { fig.handle_close(fig, {}); } );\n", + " button.mouseover('Close figure', toolbar_mouse_event);\n", + " buttongrp.append(button);\n", + " var titlebar = this.root.find($('.ui-dialog-titlebar'));\n", + " titlebar.prepend(buttongrp);\n", + "}\n", + "\n", + "\n", + "mpl.figure.prototype._canvas_extra_style = function(el){\n", + " // this is important to make the div 'focusable\n", + " el.attr('tabindex', 0)\n", + " // reach out to IPython and tell the keyboard manager to turn it's self\n", + " // off when our div gets focus\n", + "\n", + " // location in version 3\n", + " if (IPython.notebook.keyboard_manager) {\n", + " IPython.notebook.keyboard_manager.register_events(el);\n", + " }\n", + " else {\n", + " // location in version 2\n", + " IPython.keyboard_manager.register_events(el);\n", + " }\n", + "\n", + "}\n", + "\n", + "mpl.figure.prototype._key_event_extra = function(event, name) {\n", + " var manager = IPython.notebook.keyboard_manager;\n", + " if (!manager)\n", + " manager = IPython.keyboard_manager;\n", + "\n", + " // Check for shift+enter\n", + " if (event.shiftKey && event.which == 13) {\n", + " this.canvas_div.blur();\n", + " event.shiftKey = false;\n", + " // Send a \"J\" for go to next cell\n", + " event.which = 74;\n", + " event.keyCode = 74;\n", + " manager.command_mode();\n", + " manager.handle_keydown(event);\n", + " }\n", + "}\n", + "\n", + "mpl.figure.prototype.handle_save = function(fig, msg) {\n", + " fig.ondownload(fig, null);\n", + "}\n", + "\n", + "\n", + "mpl.find_output_cell = function(html_output) {\n", + " // Return the cell and output element which can be found *uniquely* in the notebook.\n", + " // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n", + " // IPython event is triggered only after the cells have been serialised, which for\n", + " // our purposes (turning an active figure into a static one), is too late.\n", + " var cells = IPython.notebook.get_cells();\n", + " var ncells = cells.length;\n", + " for (var i=0; i= 3 moved mimebundle to data attribute of output\n", + " data = data.data;\n", + " }\n", + " if (data['text/html'] == html_output) {\n", + " return [cell, data, j];\n", + " }\n", + " }\n", + " }\n", + " }\n", + "}\n", + "\n", + "// Register the function which deals with the matplotlib target/channel.\n", + "// The kernel may be null if the page has been refreshed.\n", + "if (IPython.notebook.kernel != null) {\n", + " IPython.notebook.kernel.comm_manager.register_target('matplotlib', mpl.mpl_figure_comm);\n", + "}\n" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# again, can call this repeatedly while the plot is running\n", + "swp2.sync_live()\n", + "import numpy.ma as ma\n", + "plt.figure(figsize=(12, 5))\n", + "# pcolormesh needs NaN masked out or it barfs\n", + "plt.pcolormesh(ma.masked_invalid(swp2['chan0']),\n", + " ma.masked_invalid(swp2['chan1']),\n", + " ma.masked_invalid(swp2['amplitude']),\n", + " cmap=plt.cm.hot)\n", + "plt.colorbar()\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "# I obviously have a lot to figure out about matplotlib...\n", + "# calling the above redraws constantly to \"live update\"\n", + "# generates warnings after a while about too many open figures\n", + "plt.close('all')" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.5.0" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/objects.md b/objects.md new file mode 100644 index 000000000000..d30055d9d0ba --- /dev/null +++ b/objects.md @@ -0,0 +1,135 @@ +# Object Hierarchy + +## Rough linkages: + +In **bold** the containing class creates this object. +In *italics* the container just holds this object (or class) as a default for derivatives to use. +Normal text the container includes and uses this object + +- Station + - BaseInstrument: IPInstrument, VisaInstrument, MockInstrument + - **Parameter** + - Validator: Anything, Strings, Numbers, Ints, Enum, MultiType + - **SweepValues**: SweepFixedValues, AdaptiveSweep + - Function + - Validator + - *SweepStorage class*: MergedCSVStorage, ? + - **Monitor** + - MeasurementSet + - StorageManager, Monitor + - *SweepStorage class* + - .sweep + - SweepValues + - **SweepStorage** + - **StorageManager** + - **StorageServer** + - SweepStorage + - Monitor + +## Station + +A representation of the entire physical setup. + +Lists all the connected `Instrument`s, keeps references to the `StorageManager` and +`Monitor` objects that are running, and holds various default settings, such as +the default `MeasurementSet` and the default `SweepStorage` class in use right now. + +## Instrument + +A representation of one particular piece of hardware. + +Lists all `Parameter`s and `Function`s this hardware is capable of, and handles the +underlying communication with the hardware. +`BaseInstrument` sets the common structure but real instruments will generally derive +from its subclasses, such as `IPInstrument` and `VisaInstrument`. There is also +`MockInstrument` for making simulated instruments, connected to a `Model` that mimics +a serialized communication channel with an apparatus. + +## Parameter + +A representation of one particular state variable. + +Most `Parameter`s are part of an `Instrument`, but you can also create `Parameter`s +that execute arbitrary functions, for example to combine several gate voltages in a +diagonal sweep. Parameters can have setters and/or getters (they must define at least +a setter OR a getter but do not need to define both) + +`Parameter`s can be sliced to create a `SweepFixedValues` object. + +## Validator + +Defines a set of valid inputs, and tests values against this set + +Subclasses include `Anything`, `Strings`, `Numbers`, `Ints`, `Enum`, and `MultiType`, +each of which takes various arguments that restrict it further. + +## SweepValues + +An iterator that provides values to a sweep, along with a setter for those values +connected to a `Parameter`. + +Mostly the `SweepFixedValues` subclass is used (this is +flexible enough to iterate over any arbitrary collection of values, not necessarily +linearly spaced) but other subclasses can execute adaptive sampling techniques, based +on the `.feedback` method by which the sweep passes measured values back into the +`SweepValues` object. + +## Function + +A representation of some action an `Instrument` can perform, which is not connected to +any particular state variable (eg calibrate, reset, identify) + +## StorageManager + +The gateway to the separate storage process. + +Sweeps and monitoring routines all pass their data through the `StorageManager` to the +`StorageServer`, which the `StorageManager` started and is running in a separate process. +Likewise, plotting & display routines query the `StorageServer` via the `StorageManager` +to retrieve current data. + +## StorageServer + +Running in its own process, receives, holds, and returns current sweep and monitor data, +and writes it to disk (or other storage) + +When a sweep is *not* running, the StorageServer also calls the monitor routine itself. +But when a sweep *is* running, the sweep calls the monitor so that it can avoid conflicts. +Also while a sweep is running, there are complementary `SweepStorage` objects in the sweep +and `StorageServer` processes - they are nearly identical objects, but are configured +differently. + +## Monitor + +Measures all system parameters periodically and logs these measurements + +Not yet implemented, so I don't know the details, but the plan is that during idle times +it measures parameters one-by-one, keeping track of how long it took to measure each one, +so that during sweeps it can be given a command "measure for no longer than X seconds" and +it will figure out which parameters it has time to measure, and which of those are most +important (primarily, I guess, the most important were last measured the longest time ago) + +## MeasurementSet + +A list of parameters to measure while sweeping, along with the sweep methods. + +Usually created from a `Station` and inheriting the `StorageManager`, `Monitor`, +and default `SweepStorage` class from it. + +The `.sweep` method starts a sweep, which creates a `SweepStorage` object and, +if the sweep is to run in the background (the default), this method starts the +sweep process. + +## SweepStorage + +Object that holds sweep data and knows how to read from and write to disk or +other long-term storage, as well as how to communicate with its clone on the +`StorageServer` if one exists. + +A `SweepStorage` is created when a new sweep is started, and this one clones +itself on the `StorageServer`. But you can also create a `SweepStorage` from +a previous sweep, in which case it simply reads in the sweep and holds it +for plotting or analysis. + +Subclasses (eg `MergedCSVStorage`, later perhaps `AzureStorage` etc) define +the connection with the particular long-term storage you are using. diff --git a/qcodes/__init__.py b/qcodes/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/qcodes/instrument/__init__.py b/qcodes/instrument/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/qcodes/instrument/base.py b/qcodes/instrument/base.py new file mode 100644 index 000000000000..393d939dd6f0 --- /dev/null +++ b/qcodes/instrument/base.py @@ -0,0 +1,141 @@ +import asyncio + +from qcodes.utils.metadata import Metadatable +from qcodes.utils.sync_async import wait_for_async +from qcodes.instrument.parameter import Parameter +from qcodes.instrument.function import Function + + +class BaseInstrument(Metadatable): + def __init__(self, name, **kwargs): + super().__init__(**kwargs) + self.functions = {} + self.parameters = {} + + self.name = str(name) + # TODO: need a sync/async, multiprocessing-friendly lock + # should be based on multiprocessing.Lock (or RLock) + # but with a non-blocking option for async use + # anyway threading.Lock is unpicklable on Windows + # self.lock = threading.Lock() + + def add_parameter(self, name, **kwargs): + ''' + binds one Parameter to this instrument. + + instrument subclasses can call this repeatedly in their __init__ + for every real parameter of the instrument. + + In this sense, parameters are the state variables of the instrument, + anything the user can set and/or get + + `name` is how the Parameter will be stored within instrument.parameters + and also how you address it using the shortcut methods: + instrument.set(param_name, value) etc. + + see Parameter for the list of kwargs + ''' + if name in self.parameters: + raise KeyError('Duplicate parameter name {}'.format(name)) + self.parameters[name] = Parameter(self, name, **kwargs) + + def add_function(self, name, **kwargs): + ''' + binds one Function to this instrument. + + instrument subclasses can call this repeatedly in their __init__ + for every real function of the instrument. + + In this sense, functions are actions of the instrument, that typically + transcend any one parameter, such as reset, activate, or trigger. + + `name` is how the Function will be stored within instrument.functions + and also how you address it using the shortcut methods: + instrument.call(func_name, *args) etc. + + see Function for the list of kwargs + ''' + if name in self.functions: + raise KeyError('Duplicate function name {}'.format(name)) + self.functions[name] = Function(self, name, **kwargs) + + def snapshot_base(self): + return { + 'parameters': dict((name, param.snapshot()) + for name, param in self.parameters.items()), + 'functions': dict((name, func.snapshot()) + for name, func in self.functions.items()) + } + + ########################################################################## + # write, read, and ask are the interface to hardware # + # # + # at least one (sync or async) of each pair should be overridden by a # + # subclass. These defaults simply convert between sync and async if only # + # one is defined, but raise an error if neither is. # + ########################################################################## + + def write(self, cmd): + wait_for_async(self.write_async, cmd) + + @asyncio.coroutine + def write_async(self, cmd): + # check if the paired function is still from the base class (so we'd + # have a recursion loop) notice that we only have to do this in one + # of the pair, because the other will call this one. + if self.write.__func__ is BaseInstrument.write: + raise NotImplementedError( + 'instrument {} has no write method defined'.format(self.name)) + self.write(cmd) + + def read(self): + return wait_for_async(self.read_async) + + @asyncio.coroutine + def read_async(self): + if self.read.__func__ is BaseInstrument.read: + raise NotImplementedError( + 'instrument {} has no read method defined'.format(self.name)) + return self.read() + + def ask(self, cmd): + return wait_for_async(self.ask_async, cmd) + + @asyncio.coroutine + def ask_async(self, cmd): + if self.ask.__func__ is BaseInstrument.ask: + raise NotImplementedError( + 'instrument {} has no ask method defined'.format(self.name)) + return self.ask(cmd) + + ########################################################################## + # shortcuts to parameters & setters & getters # + # # + # instrument['someparam'] === instrument.parameters['someparam'] # + # instrument.get('someparam') === instrument['someparam'].get() # + # etc... # + ########################################################################## + + def __getitem__(self, key): + return self.parameters[key] + + def set(self, param_name, value): + self.parameters[param_name].set(value) + + @asyncio.coroutine + def set_async(self, param_name, value): + yield from self.parameters[param_name].set_async(value) + + def get(self, param_name): + return self.parameters[param_name].get() + + @asyncio.coroutine + def get_async(self, param_name): + return (yield from self.parameters[param_name].get_async()) + + def call(self, func_name, *args): + return self.functions[func_name].call(*args) + + @asyncio.coroutine + def call_async(self, func_name, *args): + return (yield from self.functions[func_name].call_async(*args)) diff --git a/qcodes/instrument/function.py b/qcodes/instrument/function.py new file mode 100644 index 000000000000..fcdcc385d033 --- /dev/null +++ b/qcodes/instrument/function.py @@ -0,0 +1,75 @@ +import asyncio + +from qcodes.utils.metadata import Metadatable +from qcodes.utils.sync_async import syncable_command +from qcodes.utils.validators import Validator + + +class Function(Metadatable): + def __init__(self, instrument, name, call_cmd=None, async_call_cmd=None, + parameters=[], parse_function=None, **kwargs): + ''' + defines a function (with arbitrary parameters) that this instrument + can execute. + + instrument: an instrument that handles this function + name: the local name of this parameter + call_cmd: command to execute on instrument + - a string (with positional fields to .format, "{}" or "{0}" etc) + - a function (with parameter count matching parameters list) + async_call_cmd: an async function to use for call_async, or for both + sync and async if call_cmd is missing or None. + parameters: list of Validator objects, + one for each parameter to the Function + parse_function: function to parse the return value of cmd, + may be a type casting function like int or float. + If None, will not wait for or read any response + ''' + super().__init__(**kwargs) + + self._instrument = instrument + self.name = name + + self._set_params(parameters) + self._set_call(call_cmd, async_call_cmd, parse_function) + + def _set_params(self, parameters): + for param in parameters: + if not isinstance(param, Validator): + raise TypeError('all parameters must be Validator objects') + self._parameters = parameters + self._param_count = len(parameters) + + def _set_call(self, call_cmd, async_call_cmd, parse_function): + ask_or_write = self._instrument.write + ask_or_write_async = self._instrument.write_async + if isinstance(call_cmd, str) and parse_function: + ask_or_write = self._instrument.ask + ask_or_write_async = self._instrument.ask_async + + self._call, self._call_async = syncable_command( + self._param_count, call_cmd, async_call_cmd, + ask_or_write, ask_or_write_async, parse_function) + + def validate(self, args): + if len(args) != self._param_count: + raise TypeError( + '{} called with {} parameters but requires {}'.format( + self.name, len(args), self._param_count)) + + for i in range(self._param_count): + value = args[i] + param = self._parameters[i] + if not param.is_valid(value): + raise ValueError( + '{} is not a valid value for parameter {} of {}'.format( + value, i, self.name)) + + def call(self, *args): + self.validate(args) + return self._call(*args) + + @asyncio.coroutine + def call_async(self, *args): + self.validate(args) + return (yield from self._call_async(*args)) diff --git a/qcodes/instrument/ip.py b/qcodes/instrument/ip.py new file mode 100644 index 000000000000..600ce7ea4d2e --- /dev/null +++ b/qcodes/instrument/ip.py @@ -0,0 +1,38 @@ +import socket +import asyncio + +from qcodes.instrument.base import BaseInstrument + + +class IPInstrument(BaseInstrument): + def __init__(self, name, address=None, port=None, + timeout=5, terminator='\n', **kwargs): + super().__init__(name, **kwargs) + + self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self.socket.connect((address, port)) + + self.set_timeout(timeout) + self.terminator = terminator + + def set_timeout(self, timeout): + self.socket.settimeout(float(timeout)) + self._timeout = timeout + + @asyncio.coroutine + def write_async(self, cmd): + data = cmd + self.terminator + self.socket.send(data.encode()) + + @asyncio.coroutine + def ask_async(self, cmd): + data = cmd + self.terminator + self.socket.send(data.encode()) + # TODO: async? what's the 512? + # TODO: and why did athena *always* have a recv, even if there's + # no return? + # if this is an instrument-specific thing (like some instruments always + # reply "OK" to set commands) then these can override write_async + # to ask_async with response validation + # TODO: does this need a lock too? probably... + return self.socket.recv(512).decode() diff --git a/qcodes/instrument/mock.py b/qcodes/instrument/mock.py new file mode 100644 index 000000000000..3b825aaef055 --- /dev/null +++ b/qcodes/instrument/mock.py @@ -0,0 +1,118 @@ +import asyncio +import time +from datetime import datetime + +from qcodes.instrument.base import BaseInstrument + + +class MockInstrument(BaseInstrument): + def __init__(self, name, delay=0, model=None, keep_history=True, + use_async=False, read_response=None, **kwargs): + ''' + Creates a software instrument, for modeling or testing + inputs: + name: (string) the name of this instrument + delay: the time (in seconds) to wait after any operation + to simulate communication delay + model: an object with write and ask methods, taking 2 or 3 args: + instrument: the name of the instrument + parameter: the name of the parameter + value (write only): the value to write, as a string + + parameters to pass to model should be declared with: + get_cmd = param_name + '?' + set_cmd = param_name + ' {:.3f}' (specify the format & precision) + alternatively independent functions may still be provided. + ''' + super().__init__(name, **kwargs) + + if not isinstance(delay, (int, float)) or delay < 0: + raise TypeError('delay must be a non-negative number') + self._delay = delay + + # try to access write and ask so we know they exist + model.write + model.ask + self._model = model + + # keep a record of every command sent to this instrument + # for debugging purposes + if keep_history: + self.keep_history = True + self.history = [] + + # do we want a sync or async model? + if use_async: + self.write_async = self._write_async + self.read_async = self._read_async + self.ask_async = self._ask_async + else: + self.write = self._write + self.read = self._read + self.ask = self._ask + + # just for test purposes + self._read_response = read_response + + def _write_inner(self, cmd): + try: + parameter, value = cmd.split(' ', 1) + except ValueError: + parameter, value = cmd, None # for functions with no value + + if self.keep_history: + self.history.append((datetime.now().strftime('%Y-%m-%d %H:%M:%S'), + 'write', parameter, value)) + + self._model.write(instrument=self.name, parameter=parameter, + value=value) + + def _write(self, cmd): + if self._delay: + time.sleep(self._delay) + + self._write_inner(cmd) + + @asyncio.coroutine + def _write_async(self, cmd): + if self._delay: + yield from asyncio.sleep(self._delay) + + self._write_inner(cmd) + + def _ask_inner(self, cmd): + parameter, blank = cmd.split('?') + if blank: + raise ValueError('text found after end of query') + + if self.keep_history: + self.history.append((datetime.now().strftime('%Y-%m-%d %H:%M:%S'), + 'ask', parameter)) + + return self._model.ask(instrument=self.name, parameter=parameter) + + def _read(self): + if self._delay: + time.sleep(self._delay) + + return self._read_response + + @asyncio.coroutine + def _read_async(self): + if self._delay: + yield from asyncio.sleep(self._delay) + + return self._read_response + + def _ask(self, cmd): + if self._delay: + time.sleep(self._delay) + + return self._ask_inner(cmd) + + @asyncio.coroutine + def _ask_async(self, cmd): + if self._delay: + yield from asyncio.sleep(self._delay) + + return self._ask_inner(cmd) diff --git a/qcodes/instrument/parameter.py b/qcodes/instrument/parameter.py new file mode 100644 index 000000000000..68d3175df3cf --- /dev/null +++ b/qcodes/instrument/parameter.py @@ -0,0 +1,225 @@ +from datetime import datetime, timedelta +import time +import asyncio +import logging + +from qcodes.utils.helpers import permissive_range, wait_secs +from qcodes.utils.metadata import Metadatable +from qcodes.utils.sync_async import syncable_command, NoCommandError +from qcodes.utils.validators import Validator, Numbers, Ints +from qcodes.sweep_values import SweepFixedValues + + +def no_func(*args, **kwargs): + raise NotImplementedError('no function defined') + + +class Parameter(Metadatable): + def __init__(self, instrument, name, + get_cmd=None, async_get_cmd=None, parse_function=None, + set_cmd=None, async_set_cmd=None, vals=None, + sweep_step=None, sweep_delay=None, max_val_age=3600, + **kwargs): + ''' + defines one measurement parameter + + instrument: an instrument that handles this parameter + name: the local name of this parameter + get_cmd: a string or function to get this parameter + async_get_cmd: a function to use for async get, or for both sync + and async if get_cmd is missing or None + parse_function: function to transform the response from get + to the final output value. + NOTE: only applies if get_cmd is a string. The function forms + of get_cmd and async_get_cmd should do their own parsing + set_cmd: command to set this parameter, either: + - a string (containing one field to .format, like "{}" etc) + - a function (of one parameter) + async_set_cmd: a function to use for async set, or for both sync + and async if set_cmd is missing or None + vals: a Validator object for this parameter + sweep_step: max increment of parameter value - larger changes + are broken into steps this size + sweep_delay: time (in seconds) to wait after each sweep step + max_val_age: max time (in seconds) to trust a saved value from + this parameter as the starting point of a sweep + ''' + super().__init__(**kwargs) + + self._instrument = instrument + self.name = name + + # stored value from last .set() or .get() + # normally only used by set with a sweep, to avoid + # having to call .get() for every .set() + self._max_val_age = 0 + self._last_value = None + self._last_ts = None + + self.has_get = False + self.has_set = False + + self._set_get(get_cmd, async_get_cmd, parse_function) + self._set_vals(vals) + self._set_set(set_cmd, async_set_cmd) + self.set_sweep(sweep_step, sweep_delay, max_val_age) + + if not (self.has_get or self.has_set): + raise NoCommandError('neither set nor get cmd found in' + + ' Parameter {}'.format(self.name)) + + def snapshot_base(self): + snap = {} + if self._last_value is not None: + snap['value'] = self._last_value + snap['ts'] = self._last_ts.strftime('%Y-%m-%d %H:%M:%S') + return snap + + def _save_val(self, value): + self._last_value = value + self._last_ts = datetime.now() + + def get(self): + value = self._get() + self._save_val(value) + return value + + @asyncio.coroutine + def get_async(self): + value = yield from self._get_async() + self._save_val(value) + return value + + def _set_get(self, get_cmd, async_get_cmd, parse_function): + self._get, self._get_async = syncable_command( + 0, get_cmd, async_get_cmd, self._instrument.ask, + self._instrument.ask_async, parse_function, no_func) + + if self._get is not no_func: + self.has_get = True + + def _set_vals(self, vals): + if vals is None: + self._vals = Numbers() + elif isinstance(vals, Validator): + self._vals = vals + else: + raise TypeError('vals must be a Validator') + + def _set_set(self, set_cmd, async_set_cmd): + # note: this does not set the final setter functions. that's handled + # in self.set_sweep, when we choose a swept or non-swept setter. + self._set, self._set_async = syncable_command( + 1, set_cmd, async_set_cmd, self._instrument.write, + self._instrument.write_async, no_cmd_function=no_func) + + if self._set is not no_func: + self.has_set = True + + def validate(self, value): + if not self._vals.is_valid(value): + raise ValueError( + '{} is not a valid value for {}'.format(value, self.name)) + + def _validate_and_set(self, value): + self.validate(value) + self._set(value) + self._save_val(value) + + @asyncio.coroutine + def _validate_and_set_async(self, value): + self.validate(value) + yield from self._set_async(value) + self._save_val(value) + + def _sweep_steps(self, value): + oldest_ok_val = datetime.now() - timedelta(seconds=self._max_val_age) + if self._last_ts is None or self._last_ts < oldest_ok_val: + self.get() + start_value = self._last_value + + self.validate(start_value) + + if not (isinstance(start_value, (int, float)) and + isinstance(value, (int, float))): + # something weird... parameter is numeric but one of the ends + # isn't, even though it's valid. + # probably a MultiType with a mix of numeric and non-numeric types + # just set the endpoint and move on + logging.warning('cannot sweep {} from {} to {} - jumping.'.format( + self.name, start_value, value)) + return [] + + # drop the initial value, we're already there + return permissive_range(start_value, value, self._sweep_step)[1:] + + def _validate_and_sweep(self, value): + self.validate(value) + step_finish_ts = datetime.now() + + for step_val in self._sweep_steps(value): + self._set(step_val) + self._save_val(step_val) + step_finish_ts += timedelta(seconds=self._sweep_delay) + time.sleep(wait_secs(step_finish_ts)) + + self._set(value) + self._save_val(value) + + @asyncio.coroutine + def _validate_and_sweep_async(self, value): + self.validate(value) + step_finish_ts = datetime.now() + + for step_val in self._sweep_steps(value): + yield from self._set_async(step_val) + self._save_val(step_val) + step_finish_ts += timedelta(seconds=self._sweep_delay) + yield from asyncio.sleep(wait_secs(step_finish_ts)) + + yield from self._set_async(value) + self._save_val(value) + + def set_sweep(self, sweep_step, sweep_delay, max_val_age=None): + if sweep_step is not None or sweep_delay is not None: + if not self._vals.is_numeric: + raise TypeError('you can only sweep numeric parameters') + + if (isinstance(self._vals, Ints) and + not isinstance(sweep_step, int)): + raise TypeError( + 'sweep_step must be a positive int for an Ints parameter') + elif not isinstance(sweep_step, (int, float)): + raise TypeError('sweep_step must be a positive number') + if sweep_step <= 0: + raise ValueError('sweep_step must be positive') + + if not isinstance(sweep_delay, (int, float)): + raise TypeError('sweep_delay must be a positive number') + if sweep_delay <= 0: + raise ValueError('sweep_delay must be positive') + + self._sweep_step = sweep_step + self._sweep_delay = sweep_delay + + # assign the setters with a sweep + self.set = self._validate_and_sweep + self.set_async = self._validate_and_sweep_async + else: + # assign the setters as immediate jumps + self.set = self._validate_and_set + self.set_async = self._validate_and_set_async + + if max_val_age is not None: + if not isinstance(max_val_age, (int, float)): + raise TypeError('max_val_age must be a non-negative number') + if max_val_age < 0: + raise ValueError('max_val_age must be non-negative') + self._max_val_age = max_val_age + + def __getitem__(self, keys): + ''' + slice a Parameter to get a SweepValues object + to iterate over during a sweep + ''' + return SweepFixedValues(self, keys) diff --git a/qcodes/instrument/visa.py b/qcodes/instrument/visa.py new file mode 100644 index 000000000000..7d359eacf69f --- /dev/null +++ b/qcodes/instrument/visa.py @@ -0,0 +1,40 @@ +import visa +import asyncio + +from qcodes.instrument.base import BaseInstrument + + +class VisaInstrument(BaseInstrument): + def __init__(self, name, address=None, + timeout=5, terminator='', **kwargs): + super().__init__(name, **kwargs) + + self.set_address(address) + self.set_timeout(timeout) + self.set_terminator(terminator) + + def set_address(self, address): + resource_manager = visa.ResourceManager() + self.visa_handle = resource_manager.open_resource(address) + + self.visa_handle.clear() + self._address = address + + def set_terminator(self, terminator): + self.visa_handle.read_termination = terminator + self._terminator = terminator + + def set_timeout(self, timeout): + # we specify timeout always in seconds, but visa requires milliseconds + self.visa_handle.timeout = 1000.0 * timeout + self._timeout = timeout + + @asyncio.coroutine + def write_async(self, cmd): + # TODO: lock, async + self.visa_handle.write(cmd) + + @asyncio.coroutine + def ask_async(self, cmd): + # TODO: lock, async + return self.visa_handle.ask(cmd) diff --git a/qcodes/station.py b/qcodes/station.py new file mode 100644 index 000000000000..478d141517f8 --- /dev/null +++ b/qcodes/station.py @@ -0,0 +1,312 @@ +import time +from datetime import datetime, timedelta +import asyncio +import multiprocessing as mp + +from qcodes.utils.metadata import Metadatable +from qcodes.utils.helpers import make_unique, wait_secs +from qcodes.utils.sync_async import mock_sync +from qcodes.storage import get_storage_manager +from qcodes.sweep_storage import MergedCSVStorage + + +class Station(Metadatable): + default = None + + def __init__(self, *instruments, storage_manager=None, + storage_class=MergedCSVStorage, monitor=None, default=True): + # when a new station is defined, store it in a class variable + # so it becomes the globally accessible default station for + # new sweeps etc after that. You can still have multiple stations + # defined, but to use other than the default one you must specify + # it explicitly in the MeasurementSet + # but if for some reason you want this new Station NOT to be the + # default, just specify default=False + if default: + Station.default = self + + self.instruments = {} + for instrument in instruments: + self.add_instrument(instrument, instrument.name) + + self.storage_manager = storage_manager or get_storage_manager() + self.storage_class = storage_class + self.monitor = monitor + + def add_instrument(self, instrument, name): + name = make_unique(str(name), self.instruments) + self.instruments[name] = instrument + return name + + def set_measurement(self, *args, **kwargs): + ''' + create a MeasurementSet linked to this Station + and set it as the default for this station + ''' + self._measurement_set = MeasurementSet(*args, station=self, **kwargs) + return self._measurement_set + + def sweep(self, *args, **kwargs): + ''' + run a sweep using the default MeasurementSet for this Station + ''' + return self._measurement_set.sweep(*args, **kwargs) + + def __getitem__(self, key): + ''' + station['someinstrument'] + is a shortcut to: + station.instruments['someinstrument'] + ''' + return self.instruments[key] + + +def get_bg_sweep(): + processes = mp.active_children() + sweeps = [p for p in processes if getattr(p, 'is_sweep', False)] + + if len(sweeps) == 1: + return sweeps[0] + + if len(sweeps): + raise RuntimeError( + 'Oops, multiple sweeps are running, how did that happen?') + + return None + + +class MeasurementSet(object): + ''' + create a collection of parameters to measure and sweep + + args are a list of parameters, which can be instrument Parameters + or any other object with `get` and `get_async` methods + or they can be strings of the form '{instrument}:{parameter}' + as they are known to this MeasurementSet's linked Station + ''' + + HALT = 'HALT SWEEP' + + def __init__(self, *args, station=None, storage_manager=None, + monitor=None, storage_class=None): + self._station = station + if station: + self._storage_manager = storage_manager or station.storage_manager + self._monitor = monitor or station.monitor + self._storage_class = storage_class or station.storage_class + else: + self._storage_manager = storage_manager + self._monitor = monitor + self._storage_class = storage_class + self._parameters = [self._pick_param(arg) for arg in args] + + self.sweep_process = None + + self.signal_queue = mp.Queue() # for communicating with bg sweep + + def _pick_param(self, param_or_string): + if isinstance(param_or_string, str): + instrument_name, param_name = param_or_string.split(':') + return self._station[instrument_name][param_name] + else: + return param_or_string + + def get(self): + return tuple(p.get() for p in self._parameters) + + @asyncio.coroutine + def get_async(self): + outputs = (p.get_async() for p in self._parameters) + return (yield from asyncio.gather(*outputs)) + + def sweep(self, *args, location=None, storage_class=None, + background=True, use_async=True, enqueue=False): + ''' + execute a sweep, measuring this MeasurementSet at each point + + args: + SweepValues1, delay1, SweepValues2, delay2, ... + The first two are the outer loop, the next two are + nested inside it, etc + location: the location of the dataset, a string whose meaning + depends on the particular SweepStorage class we're using + storage_class: subclass of SweepStorage to use for storing this sweep + background: (default True) run this sweep in a separate process + so we can have live plotting and other analysis in the main process + use_async: (default True): execute the sweep asynchronously as much + as possible + enqueue: (default False): wait for a previous background sweep to + finish? If false, will raise an error if another sweep is running + + returns: + a SweepStorage object that we can use to plot + ''' + + prev_sweep = get_bg_sweep() + + if prev_sweep: + if enqueue: + prev_sweep.join() # wait until previous sweep finishes + else: + raise RuntimeError( + 'a sweep is already running in the background') + + self._init_sweep(args, location=location, storage_class=storage_class) + + sweep_fn = mock_sync(self._sweep_async) if use_async else self._sweep + + # clear any lingering signal queue items + while not self.signal_queue.empty(): + self.signal_queue.get() + + if background: + if not self._storage_manager: + raise RuntimeError('sweep can only run in the background ' + 'if it has a storage manager running') + + # start the sweep in a new process + # TODO: in notebooks, errors in a background sweep will just appear + # the next time a command is run. Do something better? + # (like log them somewhere, show in monitoring window)? + p = mp.Process(target=sweep_fn, daemon=True) + p.start() + + # flag this as a sweep process, and connect in its storage object + # so you can always find running sweeps and data even if you + # don't have a Station or MeasurementSet + p.is_sweep = True + p.storage = self._storage + p.measurement = self + self.sweep_process = p + + else: + sweep_fn() + + self._storage.sync_live() + return self._storage + + def _init_sweep(self, args, location=None, storage_class=None): + if len(args) < 2: + raise TypeError('need at least one SweepValues, delay pair') + sweep_vals, delays = args[::2], args[1::2] + if len(sweep_vals) != len(delays): + raise TypeError('SweepValues without matching delay') + + # find the output array size we need + self._dim_size = [len(v) for v in sweep_vals] + self._sweep_params = [v.name for v in sweep_vals] + all_params = self._sweep_params + [p.name for p in self._parameters] + + # do any of the sweep params support feedback? + self._feedback = [v for v in sweep_vals if hasattr(v, 'feedback') + and callable(v.feedback)] + + if storage_class is None: + storage_class = self._storage_class + self._storage = storage_class(location, + param_names=all_params, + dim_sizes=self._dim_size, + storage_manager=self._storage_manager, + passthrough=True) + + self._sweep_def = tuple(zip(sweep_vals, delays)) + self._sweep_depth = len(sweep_vals) + + def _store(self, indices, set_values, measured): + self._storage.set_point(indices, set_values + tuple(measured)) + + # for adaptive sampling - pass this measurement back to + # any sweep param that supports feedback + for vals in self._feedback: + vals.feedback(set_values, measured) + + def _check_signal(self): + while not self.signal_queue.empty(): + signal = self.signal_queue.get() + if signal == self.HALT: + raise KeyboardInterrupt('sweep was halted') + + def _sweep(self, indices=(), current_values=()): + self._check_signal() + + current_depth = len(indices) + + if current_depth == self._sweep_depth: + measured = self.get() + self._store(indices, current_values, measured) + + else: + values, delay = self._sweep_def[current_depth] + for i, value in enumerate(values): + if i or not current_depth: + values.set(value) + + # if we're changing an outer loop variable, + # also change any inner variables before waiting + for inner_values, _ in self._sweep_def[current_depth + 1:]: + inner_values.set(inner_values[0]) + + finish_datetime = datetime.now() + timedelta(seconds=delay) + + if self._monitor: + self._monitor.call(finish_by=finish_datetime) + + self._check_signal() + time.sleep(wait_secs(finish_datetime)) + + # sweep the next level + self._sweep(indices + (i,), current_values + (value,)) + + if not current_depth: + self._storage.close() + + def _sweep_async(self, indices=(), current_values=()): + self._check_signal() + + current_depth = len(indices) + + if current_depth == self._sweep_depth: + measured = yield from self.get_async() + self._store(indices, current_values, measured) + + else: + values, delay = self._sweep_def[current_depth] + for i, value in enumerate(values): + if i or not current_depth: + setters = [values.set_async(value)] + + # if we're changing an outer loop variable, + # also change any inner variables before waiting + for inner_values, _ in self._sweep_def[current_depth + 1:]: + setters.append(inner_values.set_async(inner_values[0])) + + yield from asyncio.gather(*setters) + + finish_datetime = datetime.now() + timedelta(seconds=delay) + + if self._monitor: + yield from self._monitor.call_async( + finish_by=finish_datetime) + + self._check_signal() + yield from asyncio.sleep(wait_secs(finish_datetime)) + + # sweep the next level + yield from self._sweep_async(indices + (i,), + current_values + (value,)) + + if not current_depth: + self._storage.close() + + def halt_sweep(self, timeout=5): + sweep = get_bg_sweep() + if not sweep: + print('No sweep running') + return + + self.signal_queue.put(self.HALT) + sweep.join(timeout) + + if sweep.is_alive(): + sweep.terminate() + print('Background sweep did not respond, terminated') diff --git a/qcodes/storage.py b/qcodes/storage.py new file mode 100644 index 000000000000..ee713831b6fe --- /dev/null +++ b/qcodes/storage.py @@ -0,0 +1,197 @@ +import multiprocessing as mp +from queue import Empty +from traceback import format_exc +from datetime import datetime, timedelta + +from qcodes.sweep_storage import NoSweep + + +def get_storage_manager(): + ''' + create or retrieve the storage manager + makes sure we don't accidentally create multiple StorageManager processes + ''' + sm = StorageManager.default + if sm and sm._server.is_alive(): + return sm + return StorageManager() + + +class StorageManager(object): + default = None + ''' + creates a separate process (StorageServer) that holds running sweeps + and monitor data, and manages writing these to disk or other storage + + To talk to the server, get a client with StorageManager.new_client() + + StorageServer communicates with other processes through messages + I'll write this using multiprocessing Queue's, but should be easily + extensible to other messaging systems + ''' + def __init__(self, query_timeout=2): + StorageManager.default = self + + self._query_queue = mp.Queue() + self._response_queue = mp.Queue() + self._error_queue = mp.Queue() + + # lock is only used with queries that get responses + # to make sure the process that asked the question is the one + # that gets the response. + self._query_lock = mp.RLock() + + self.query_timeout = query_timeout + self._start_server() + + def _start_server(self): + self._server = mp.Process(target=self._run_server, daemon=True) + self._server.start() + + def _run_server(self): + StorageServer(self._query_queue, self._response_queue, + self._error_queue) + + def write(self, *query): + self._query_queue.put(query) + self.check_for_errors() + + def check_for_errors(self): + if not self._error_queue.empty(): + raise self._error_queue.get() + + def ask(self, *query, timeout=None): + timeout = timeout or self.query_timeout + + with self._query_lock: + self._query_queue.put(query) + try: + res = self._response_queue.get(timeout=timeout) + except Empty as e: + if self._error_queue.empty(): + # only raise if we're not about to find a deeper error + # I do it this way rather than just checking for errors + # now) because ipython obfuscates the real error + # by noting that it occurred while processing this one + raise e + self.check_for_errors() + + return res + + def halt(self): + if self._server.is_alive(): + self.write('halt') + self._server.join() + + def restart(self): + self.halt() + self._start_server() + + def set(self, key, value): + self.write('set', key, value) + + def get(self, key, timeout=None): + return self.ask('get', key, timeout=timeout) + + +class StorageServer(object): + default_storage_period = 1 # seconds between data storage calls + queries_per_store = 5 + default_monitor_period = 60 # seconds between monitoring storage calls + + def __init__(self, query_queue, response_queue, error_queue): + self._query_queue = query_queue + self._response_queue = response_queue + self._error_queue = error_queue + self._storage_period = self.default_storage_period + self._monitor_period = self.default_monitor_period + + # flexible storage, for testing purposes + # but we can adapt this when we get to monitoring + self._dict = {} + + self._sweep = NoSweep() + self._sweeping = False + + self._run() + + def _run(self): + self._running = True + next_store_ts = datetime.now() + next_monitor_ts = datetime.now() + + while self._running: + read_timeout = self._storage_period / self.queries_per_store + try: + query = self._query_queue.get(timeout=read_timeout) + getattr(self, 'handle_' + query[0])(*(query[1:])) + except Empty: + pass + except Exception as e: + self._post_error(e) + + try: + now = datetime.now() + + if now > next_store_ts: + td = timedelta(seconds=self._storage_period) + next_store_ts = now + td + self._sweep.update_storage_wrapper() + + if now > next_monitor_ts: + td = timedelta(seconds=self._monitor_period) + next_monitor_ts = now + td + # TODO: update the monitor data storage + + except Exception as e: + self._post_error(e) + + def _reply(self, response): + self._response_queue.put(response) + + def _post_error(self, e): + e.args = e.args + (format_exc(), ) + self._error_queue.put(e) + + ###################################################################### + # query handlers # + # # + # method: handle_(self, arg1, arg2, ...) # + # will capture queries ('', arg1, arg2, ...) # + ###################################################################### + + # _dict, set, get, and call are all just for testing/debugging + def handle_set(self, key, value): + self._dict[key] = value + + def handle_get(self, key): + self._reply(self._dict[key]) + + def handle_call(self, key, method_name, args): + self._reply(getattr(self._dict[key], method_name)(*args)) + + def handle_halt(self): + self._running = False + + def handle_new_sweep(self, sweep_storage_obj): + if self._sweeping: + raise RuntimeError('Already executing a sweep') + self._sweep = sweep_storage_obj + self._sweep.init_on_server() + self._sweeping = True + # send a reply, just so the client expects a response + # and therefore can listen for errors + self._reply(True) + + def handle_end_sweep(self): + self._sweep.update_storage_wrapper() + self._sweeping = False + + def handle_set_sweep_point(self, indices, values): + self._sweep.set_point(indices, values) + + def handle_get_sweeping(self): + self._reply(self._sweeping) + + def handle_get_sweep(self, attr=None): + self._reply(self._sweep.get(attr)) diff --git a/qcodes/sweep_storage.py b/qcodes/sweep_storage.py new file mode 100644 index 000000000000..97e19315ed63 --- /dev/null +++ b/qcodes/sweep_storage.py @@ -0,0 +1,329 @@ +import math +import numpy as np +from datetime import datetime +import time +import csv +import re + + +class NoSweep(object): + def data_point(self, *args, **kwargs): + raise RuntimeError('no sweep to add data to') + + def update_storage_wrapper(self, *args, **kwargs): + pass + + +class SweepStorage(object): + ''' + base class for holding and storing sweep data + subclasses should override update_storage and read + + inputs: + location: a string, can represent different things + depending on what the storage physically represents. + can be a folder path (if the data is in separate files) + or a file path (if all the data is in one file) + or some other resource locator (for azure, AWS, etc) + param_names: a sequence of strings listing all parameters to store, + starting with the sweep parameters, from outer to innermost loop + dim_sizes: a sequence containing the size of each dimension of + the sweep, from outer to innermost loop + storage_manager: if we're given a StorageManager here, then this + instance is to be potentially connected to the storage server + process via this storage_manager: + If this is a new sweep, we create a mirror of it on the server. + If it's an existing sweep, we check whether it's the current live + sweep on the server, then allow syncing data from there + passthrough: if True and we have a storage_manager, don't keep a copy + of the data in this object, only in the server copy + + param_names and dim_sizes ust be provided if and only if this is a + new sweep. If omitted, location must reference an existing sweep + ''' + def __init__(self, location, param_names=None, dim_sizes=None, + storage_manager=None, passthrough=False): + self.location = location # TODO: auto location for new sweeps? + + self._passthrough = storage_manager and passthrough + + if param_names and dim_sizes: + self._init_new_sweep(param_names, dim_sizes, storage_manager) + + elif param_names is None and dim_sizes is None: + # omitted names and dim_sizes? we're reading from a saved file + self._init_existing_sweep() + else: + raise TypeError('you must provide either both or neither of ' + 'param_names and dim_sizes') + + # need to set storage_manager *after* _init_new_sweep + # because we can't send storage_manager through a queue + # to the storage_manager itself + self._storage_manager = storage_manager + + def _init_new_sweep(self, param_names, dim_sizes, storage_manager): + self.init_data(param_names, dim_sizes) + self.new_indices = set() + self.last_saved_index = -1 + + if storage_manager: + # If this class was not available when storage_manager was started, + # we can't unpickle it on the other end. + # So we'll try, then restart if this error occurs, then try again. + # + # This still has a pitfall, if the class has been *changed* since + # starting the server, it will still have the old version. + # If the user does that, they need to manually restart the server, + # using: + # storage_manager.restart() + try: + # The copy to be sent to the server should NOT be marked as + # syncable with the server, it's ON the server! + self._sync_to_server = False + storage_manager.ask('new_sweep', self) + except AttributeError: + storage_manager.restart() + storage_manager.ask('new_sweep', self) + + self._sync_to_server = True + + else: + self._sync_to_server = False + + def _init_existing_sweep(self): + initialized = False + if self._storage_manager: + with self._storage_manager._query_lock: + if self.check_live_sweep(): + live_obj = self._storage_manager.ask('get_sweep') + self.init_data(live_obj.param_names, + live_obj.dim_sizes, + live_obj.data) + initialized = True + + if not initialized: + self._sync_to_server = False + self.read() + + def init_on_server(self): + ''' + any changes we have to make when this object first arrives + on the storage server? + ''' + if self._passthrough: + # this is where we're passing FROM! + # so turn off passthrough here, and make the data arrays + self._passthrough = False + self.init_data(self.param_names, self.dim_sizes, + getattr(self, 'data', None)) + + def check_live_sweep(self): + if self._storage_manager: + live_location = self._storage_manager.ask('get_sweep', 'location') + self._sync_to_server = (self.location == live_location) + else: + self._sync_to_server = False + + return self._sync_to_server + + def sync_live(self): + if not (self._sync_to_server and self._storage_manager): + # assume that once we determine it's not the live sweep, + # it will never be the live sweep again + return + + self._passthrough = False + with self._storage_manager._query_lock: + self.check_live_sweep() + if not self._sync_to_server: + return + self.data = self._storage_manager.ask('get_sweep', 'data') + + def init_data(self, param_names, dim_sizes, data=None): + self.param_names = tuple(param_names) + self.dim_sizes = tuple(dim_sizes) + + if data: + self.data = data + elif not self._passthrough: + self.data = {} + for pn in self.param_names + ('ts',): + arr = np.ndarray(self.dim_sizes) + arr.fill(float("nan")) + self.data[pn] = arr + + def set_point(self, indices, values): + indices = tuple(indices) + + if self._sync_to_server: + self._storage_manager.write('set_sweep_point', indices, values) + + if not self._passthrough: + for pn, val in zip(self.param_names, values): + self.data[pn][indices] = val + + self.data['ts'][indices] = time.time() + + flat_index = np.ravel_multi_index(tuple(zip(indices)), + self.dim_sizes)[0] + self.new_indices.add(flat_index) + + def close(self): + if self._sync_to_server: + self._storage_manager.write('end_sweep') + + if not self._passthrough: + self.sync_live() + + self._sync_to_server = False + + def get(self, attr=None): + ''' + getter for use by storage server - either return the whole + object, or an attribute of it. + ''' + if attr is None: + return self + else: + return getattr(self, attr) + + def update_storage_wrapper(self): + if self._passthrough: + raise RuntimeError('This cobject has no data to save, ' + 'it\'s just a passthrough to the server.') + if not self.new_indices: + return + + self.update_storage() + + self.last_saved_index = max(self.last_saved_index, *self.new_indices) + self.new_indices = set() + + def update_storage(self): + ''' + write the data set (or changes to it) to storage + based on the data and definition attributes: + data + param_names + dim_sizes + and also info about what has changed since last write: + new_indices + last_saved_index + ''' + raise NotImplementedError + + def read(self): + ''' + read from a file into the data and definition attributes: + data (dict of numpy ndarray's) + param_names + dim_sizes + the file format is expected to provide all of this info + ''' + raise NotImplementedError + + def __getitem__(self, key): + return self.data[key] + + +class MergedCSVStorage(SweepStorage): + ''' + class that stores any sweep as a single file + with all parameters together, one line per data point + ''' + + index_head_re = re.compile('^i_.+\((\d+)\)$') + + def __init__(self, location, *args, **kwargs): + # set _path before __init__ so it can call .read if necessary + if location[-4:] == '.csv': + self._path = location + else: + self._path = location + '.csv' + + super().__init__(location, *args, **kwargs) + + def update_storage(self): + first_new_index = min(self.new_indices) + last_new_index = max(self.new_indices) + + if 0 < self.last_saved_index < first_new_index: + with open(self._path, 'a') as f: + writer = csv.writer(f) + self._writerange(writer, self.last_saved_index, + last_new_index + 1) + else: + with open(self._path, 'w') as f: + writer = csv.writer(f) + self._writeheader(writer) + self._writerange(writer, 0, last_new_index + 1) + + def _writerange(self, writer, istart, iend): + for i in range(istart, iend): + indices = np.unravel_index(i, self.dim_sizes) + + tsraw = self.data['ts'][indices] + if not math.isfinite(tsraw): + continue # don't store empty data points + + ts = datetime.fromtimestamp(tsraw) + rowhead = (ts.strftime('%Y-%m-%d %H:%M:%S:%f'),) + indices + + row = tuple(self.data[pn][indices] for pn in self.param_names) + + writer.writerow(rowhead + row) + + def _writeheader(self, writer): + loop_names = tuple('i_{}({})'.format(pn, ds) + for ds, pn in zip(self.dim_sizes, self.param_names)) + writer.writerow(('ts',) + loop_names + self.param_names) + + def _row(self, i): + indices = np.unravel_index(i, self.dim_sizes) + + ts = datetime.fromtimestamp(self.data['ts'][indices]) + + out = [ts.strftime('%Y-%m-%d %H:%M:%S:%f')] + out += [self.data[pn][indices] for pn in self.param_names] + + return out + + def read(self): + self._passthrough = False + with open(self._path, 'r') as f: + reader = csv.reader(f) + head = next(reader) + self._parse_header(head) + + dimensions = len(self.dim_sizes) + + for row in reader: + indices = tuple(map(int, row[1: dimensions + 1])) + for val, pn in zip(row[dimensions + 1:], self.param_names): + self.data[pn][indices] = val + + self.data['ts'][indices] = datetime.strptime( + row[0], '%Y-%m-%d %H:%M:%S:%f') + + def _parse_header(self, head): + if head[0] != 'ts': + raise ValueError('ts must be the first header for this format') + + dim_sizes = [] + for col in range(1, len(head)): + index_match = self.index_head_re.match(head[col]) + if not index_match: + break + dim_sizes += [int(index_match.groups()[0])] + + if not dim_sizes: + raise ValueError('no sweep dimensions found in header row') + + param_names = [] + for param_col in range(col, len(head)): + param_names.append(head[param_col]) + if not param_names: + raise ValueError('no param_names found in header row') + + self.init_data(param_names, dim_sizes) diff --git a/qcodes/sweep_values.py b/qcodes/sweep_values.py new file mode 100644 index 000000000000..9c3e8b6022e3 --- /dev/null +++ b/qcodes/sweep_values.py @@ -0,0 +1,271 @@ +from qcodes.utils.helpers import is_sequence, permissive_range +from qcodes.utils.sync_async import mock_async, mock_sync + + +class SweepValues(object): + ''' + base class for sweeping a parameter + must be subclassed to provide the sweep values + + inputs: + parameter: the target of the sweep, an object with + set (and/or set_async), and optionally validate methods + + intended use is to iterate over in a sweep, so it must support: + .__iter__ (and .__next__ if necessary). + .set and .set_async are provided by the base class + + optionally, it can have a feedback method that allows the sweep to pass + measurements back to this object for adaptive sampling: + .feedback(set_values, measured_values) + See AdaptiveSweep for an example + + example usage: + for i, value in eumerate(sv): + sv.set(value) # or (await / yield from) sv.set_async(value) + # set(_async) just shortcuts sv._parameter.set + sleep(delay) + vals = measure() + sv.feedback((i, ), vals) # optional - sweep should not assume + # .feedback exists + + note though that sweeps should only require set, set_async, and + __iter__ - ie "for val in sv", so any class that implements these + may be used in sweeps. That allows things like adaptive sampling, + where you don't know ahead of time what the values will be or even + how many there are. + ''' + def __init__(self, parameter): + self._parameter = parameter + self.name = parameter.name + self._values = [] + + # create the set and set_async shortcuts + if hasattr(parameter, 'set'): + self.set = parameter.set + else: + self.set = mock_sync(parameter.set_async) + + if hasattr(parameter, 'set_async'): + self.set_async = parameter.set_async + else: + self.set_async = mock_async(parameter.set) + + def validate(self, values): + if hasattr(self._parameter, 'validate'): + for value in values: + self._parameter.validate(value) + + def __iter__(self): + raise NotImplementedError + + +class SweepFixedValues(SweepValues): + ''' + a fixed collection of parameter values to be iterated over during a sweep. + + inputs: + parameter: the target of the sweep, an object with + set (and/or set_async), and optionally validate methods + keys: one or a sequence of items, each of which can be: + - a single parameter value + - a sequence of parameter values + - a slice object, which MUST include all three args + + a SweepFixedValues object is normally created by slicing a Parameter p: + + sv = p[1.2:2:0.01] # slice notation + sv = p[1, 1.1, 1.3, 1.6] # explicit individual values + sv = p[1.2:2:0.01, 2:3:0.02] # sequence of slices + sv = p[logrange(1,10,.01)] # some function that returns a sequence + + you can also use list operations to modify these: + + sv += p[2:3:.01] (another SweepFixedValues of the same parameter) + sv += [4, 5, 6] (a bare sequence) + sv.extend(p[2:3:.01]) + sv.append(3.2) + sv.reverse() + sv2 = reversed(sv) + sv3 = sv + sv2 + sv4 = sv.copy() + + note though that sweeps should only require set, set_async, and + __iter__ - ie "for val in sv", so any class that implements these + may be used in sweeps. That allows things like adaptive sampling, + where you don't know ahead of time what the values will be or even + how many there are. + ''' + def __init__(self, parameter, keys): + super().__init__(parameter) + self._values = [] + keyset = keys if is_sequence(keys) else (keys,) + + for key in keyset: + if is_sequence(key): + self._values.extend(key) + elif isinstance(key, slice): + if key.start is None or key.stop is None or key.step is None: + raise TypeError('all 3 slice parameters are required, ' + + '{} is missing some'.format(key)) + self._values.extend(permissive_range(key.start, key.stop, + key.step)) + else: + # assume a single value + self._values.append(key) + + self.validate(self._values) + + def append(self, value): + self.validate((value,)) + self._values.append(value) + + def extend(self, new_values): + if isinstance(new_values, SweepFixedValues): + if new_values._parameter is not self._parameter: + raise TypeError( + 'can only extend SweepFixedValues of the same parameters') + # these values are already validated + self._values.extend(new_values._values) + elif is_sequence(new_values): + self.validate(new_values) + self._values.extend(new_values) + else: + raise TypeError( + 'cannot extend SweepFixedValues with {}'.format(new_values)) + + def copy(self): + new_sv = SweepFixedValues(self._parameter, []) + # skip validation by adding values separately instead of on init + new_sv._values = self._values[:] + return new_sv + + def reverse(self): + self._values.reverse() + + def __iter__(self): + return iter(self._values) + + def __getitem__(self, key): + return self._values[key] + + def __len__(self): + return len(self._values) + + def __add__(self, other): + new_sv = self.copy() + new_sv.extend(other) + return new_sv + + def __iadd__(self, values): + self.extend(values) + return self + + def __contains__(self, value): + return value in self._values + + def __reversed__(self): + new_sv = self.copy() + new_sv.reverse() + return new_sv + + +class AdaptiveSweep(SweepValues): + ''' + an example class to show how adaptive sampling might be implemented + + usage: + measurement.sweep(AdaptiveSweep(param, start, end, target_delta)) + + inputs: + start: initial parameter value + end: final parameter value + target_delta: change in the measured val to target + max_step: biggest change in parameter value allowed + min_step: smallest change allowed, so we don't sweep forever + measurement_index: which measurement parameter are we feeding back on? + ''' + def __init__(self, parameter, start, end, target_delta, max_step=None, + min_step=None, measurement_index=0): + super().__init__(parameter) + + self._start = start + self._end = end + self._direction = 1 if end > start else -1 + + self._target_delta = target_delta + + self._max_step = max_step or abs(end - start) / 100 + self._min_step = min_step or self._max_step / 100 + + self._measurement_index = measurement_index + + def __iter__(self): + ''' + start or restart the adaptive algorithm + called at the beginning of "for ... in ..." + + in principle, each iteration could base its outputs + on the previous iteration, for example to follow peaks + that move slowly as a function of the outer loop parameter. + but in this simple example we're just basing each point on the + previous two + ''' + self._setting = None + self._step = None + self._measured = None + self._delta = None + self._new_val_count = 0 + + # return self so iteration will call our own __next__ + return self + + def feedback(self, set_values, measured_values): + ''' + the sweep routine will look for a .feedback method + to pass new measurements into the SweepValues object + + it provides: + set_values: sequence of the current sweep parameters + measured_values: sequence of the measured values at this setting + ''' + self._new_val_count += 1 + if self._new_val_count > 1: + # more than one measurement per iteration means we're + # not in the inner loop. in principle one could adaptively + # sample an outer loop too, using the whole line of inner loop + # measurements, but the algorithm here only applies to the inner. + raise RuntimeError( + 'AdaptiveSweep can only be used on the inner loop') + + new_measured = measured_values[self._measurement_index] + + if self._measured is not None: + self._delta = new_measured - self._measured + + self._measured = new_measured + + def __next__(self): + self._new_val_count = 0 + + if self._setting == self._end: + # terminate the iteration if we've already set the endpoint + raise StopIteration + + # target the step so the next delta is target_delta, if data is linear + if self._delta is None: + step = self._min_step # start off slow + else: + step = abs(self._step * self._target_delta / self._delta) + # don't increase too much at once + step = max(step, self._step * 3) + + # constrain it to provide min and max + step = min(max(self._min_step, step), self._max_step) + self._setting += self._direction * step + + # stop at the end + if self._setting * self._direction > self._end * self._direction: + self._setting = self._end + + return self._setting diff --git a/qcodes/tests/__init__.py b/qcodes/tests/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/qcodes/tests/py35_syntax.py b/qcodes/tests/py35_syntax.py new file mode 100644 index 000000000000..4d603657bb82 --- /dev/null +++ b/qcodes/tests/py35_syntax.py @@ -0,0 +1,28 @@ +####################################################################### +# This file is imported into tests only if the syntax is supported # +# # +# The package itself has been downgraded to only use py 3.3 syntax # +# for asynchronous functions, but the tests will ensure that it works # +# with the newer py 3.5 syntax too # +####################################################################### + +import asyncio + +# f_async is for test_helpers +async def f_async(): + raise RuntimeError('function should not get called') + + +# async1, 2, 3_new are for test_sync_async +async def async1_new(v): + return v**2 + + +async def async2_new(v, n): + for i in range(n): + await asyncio.sleep(0.001) + return v**2 + + +async def async3_new(v, n): + return await async2_new(v, n) diff --git a/qcodes/tests/test_helpers.py b/qcodes/tests/test_helpers.py new file mode 100644 index 000000000000..4e887c8cdf7d --- /dev/null +++ b/qcodes/tests/test_helpers.py @@ -0,0 +1,208 @@ +from unittest import TestCase +import os +from datetime import datetime, timedelta +import asyncio + +from qcodes.utils.helpers import (is_function, is_sequence, permissive_range, + wait_secs, make_unique) + + +class TestIsFunction(TestCase): + def test_non_function(self): + self.assertFalse(is_function(0, 0)) + self.assertFalse(is_function('hello!', 0)) + self.assertFalse(is_function(None, 0)) + + def test_function(self): + def f0(): + raise RuntimeError('function should not get called') + + def f1(a): + raise RuntimeError('function should not get called') + + def f2(a, b): + raise RuntimeError('function should not get called') + + self.assertTrue(is_function(f0, 0)) + self.assertTrue(is_function(f1, 1)) + self.assertTrue(is_function(f2, 2)) + + self.assertFalse(is_function(f0, 1) or is_function(f0, 2)) + self.assertFalse(is_function(f1, 0) or is_function(f1, 2)) + self.assertFalse(is_function(f2, 0) or is_function(f2, 1)) + + # make sure we only accept valid arg_count + with self.assertRaises(TypeError): + is_function(f0, 'lots') + with self.assertRaises(TypeError): + is_function(f0, -1) + + class AClass(object): + def method_a(self): + raise RuntimeError('function should not get called') + + def method_b(self, v): + raise RuntimeError('function should not get called') + + @asyncio.coroutine + def method_c(self, v): + raise RuntimeError('function should not get called') + + def test_methods(self): + a = self.AClass() + self.assertTrue(is_function(a.method_a, 0)) + self.assertFalse(is_function(a.method_a, 1)) + self.assertTrue(is_function(a.method_b, 1)) + self.assertTrue(is_function(a.method_c, 1, coroutine=True)) + + def test_type_cast(self): + self.assertTrue(is_function(int, 1)) + self.assertTrue(is_function(float, 1)) + self.assertTrue(is_function(str, 1)) + + self.assertFalse(is_function(int, 0) or is_function(int, 2)) + self.assertFalse(is_function(float, 0) or is_function(float, 2)) + self.assertFalse(is_function(str, 0) or is_function(str, 2)) + + def test_coroutine_check(self): + def f_sync(): + raise RuntimeError('function should not get called') + + self.assertTrue(is_function(f_sync, 0)) + self.assertTrue(is_function(f_sync, 0, coroutine=False)) + + # support pre-py3.5 async syntax + @asyncio.coroutine + def f_async_old(): + raise RuntimeError('function should not get called') + + self.assertFalse(is_function(f_async_old, 0, coroutine=False)) + self.assertTrue(is_function(f_async_old, 0, coroutine=True)) + self.assertFalse(is_function(f_async_old, 0)) + + # test py3.5 syntax async functions + try: + from qcodes.tests.py35_syntax import f_async + py35 = True + except: + py35 = False + + if py35: + self.assertFalse(is_function(f_async, 0, coroutine=False)) + self.assertTrue(is_function(f_async, 0, coroutine=True)) + self.assertFalse(is_function(f_async, 0)) + + +class TestIsSequence(TestCase): + def a_func(): + raise RuntimeError('this function shouldn\'t get called') + + class AClass(): + pass + + def test_yes(self): + f = open(os.listdir('.')[0], 'r') + yes_sequence = [ + [], + [1, 2, 3], + range(5), + + # we do have to be careful about generators... + # ie don't call len() or iterate twice + (i**2 for i in range(5)), + + # and some possibly useless or confusing matches + # that we might want to rule out: + set((1, 2, 3)), + {1: 2, 3: 4}, + f + ] + + for val in yes_sequence: + self.assertTrue(is_sequence(val)) + + f.close() + + def test_no(self): + no_sequence = [ + 1, + 1.0, + True, + None, + 'you can iterate a string but we won\'t', + b'nor will we iterate bytes', + self.a_func, + self.AClass, + self.AClass() + ] + + for val in no_sequence: + self.assertFalse(is_sequence(val)) + + +class TestPermissiveRange(TestCase): + def test_bad_calls(self): + bad_args = [ + [], + [1], + [1, 2], + [None, 1, .1], + [1, None, .1], + [1, 2, 'not too far'] + ] + + for args in bad_args: + with self.assertRaises(Exception): + permissive_range(*args) + + def test_good_calls(self): + good_args = { + (1, 7, 2): [1, 3, 5], + (1, 7, 4): [1, 5], + (1, 7, 7): [1], + (1.0, 7, 2): [1.0, 3.0, 5.0], + (1, 7.0, 2): [1.0, 3.0, 5.0], + (1, 7, 2.0): [1.0, 3.0, 5.0], + (1.0, 7.0, 2.0): [1.0, 3.0, 5.0], + (1.0, 7.000000001, 2.0): [1.0, 3.0, 5.0, 7.0], + (1, 7, -2): [1, 3, 5], + (7, 1, 2): [7, 5, 3], + (1.0, 7.0, -2.0): [1.0, 3.0, 5.0], + (7.0, 1.0, 2.0): [7.0, 5.0, 3.0], + (7.0, 1.0, -2.0): [7.0, 5.0, 3.0], + (1.5, 1.8, 0.1): [1.5, 1.6, 1.7] + } + + for args, result in good_args.items(): + self.assertEqual(permissive_range(*args), result) + + +class TestWaitSecs(TestCase): + def test_bad_calls(self): + bad_args = [None, 1, 1.0, True] + for arg in bad_args: + with self.assertRaises(TypeError): + wait_secs(arg) + + def test_good_calls(self): + for secs in [0.001, 0.003, 0.01, 0.03, 0.1, 0.3, 1]: + finish_datetime = datetime.now() + timedelta(seconds=secs) + secs_out = wait_secs(finish_datetime) + self.assertGreater(secs_out, secs - 1e-4) + self.assertLessEqual(secs_out, secs) + + def test_warning(self): + # TODO: how to test what logging is doing? + secs_out = wait_secs(datetime.now() - timedelta(seconds=1)) + self.assertEqual(secs_out, 0) + + +class TestMakeUnique(TestCase): + def test_no_changes(self): + for s, existing in (('a', []), ('a', {}), ('a', ('A', ' a', 'a '))): + self.assertEqual(make_unique(s, existing), s) + + def test_changes(self): + self.assertEqual(make_unique('a', ('a',)), 'a_2') + self.assertEqual(make_unique('a_2', ('a_2',)), 'a_2_2') + self.assertEqual(make_unique('a', ('a', 'a_2', 'a_3')), 'a_4') diff --git a/qcodes/tests/test_instrument.py b/qcodes/tests/test_instrument.py new file mode 100644 index 000000000000..4bee3ce2d8f7 --- /dev/null +++ b/qcodes/tests/test_instrument.py @@ -0,0 +1,449 @@ +import asyncio +from unittest import TestCase +from datetime import datetime, timedelta + +from qcodes.instrument.base import BaseInstrument +from qcodes.instrument.mock import MockInstrument +from qcodes.utils.validators import Numbers, Ints, Strings, MultiType +from qcodes.utils.sync_async import wait_for_async, NoCommandError + + +class ModelError(Exception): + pass + + +class AMockModel(object): + def __init__(self): + self._gates = [0.0, 0.0, 0.0] + self._excitation = 0.1 + + def write(self, instrument, parameter, value): + if instrument == 'gates' and parameter[0] == 'c': + self._gates[int(parameter[1:])] = float(value) + elif instrument == 'gates' and parameter == 'rst': + self._gates = [0.0, 0.0, 0.0] + elif instrument == 'source' and parameter == 'ampl': + try: + self._excitation = float(value) + except: + # "Off" as in the MultiType sweep step test + self._excitation = None + else: + raise ModelError('unrecognized write {}, {}, {}'.format( + instrument, parameter, value)) + + def ask(self, instrument, parameter): + gates = self._gates + + if instrument == 'gates' and parameter[0] == 'c': + v = gates[int(parameter[1:])] + elif instrument == 'source' and parameter == 'ampl': + v = self._excitation + elif instrument == 'meter' and parameter == 'ampl': + # here's my super complex model output! + v = self._excitation * (gates[0] + gates[1]**2 + gates[2]**3) + elif instrument == 'meter' and parameter[:5] == 'echo ': + v = float(parameter[5:]) + else: + raise ModelError('unrecognized ask {}, {}'.format( + instrument, parameter)) + + return '{:.3f}'.format(v) + + +class TestParameters(TestCase): + def setUp(self): + self.model = AMockModel() + self.read_response = 'I am the walrus!' + + self.gates = MockInstrument('gates', model=self.model, delay=0.001, + use_async=True, + read_response=self.read_response) + for i in range(3): + cmdbase = 'c{}'.format(i) + self.gates.add_parameter('chan{}'.format(i), get_cmd=cmdbase + '?', + set_cmd=cmdbase + ' {:.4f}', + parse_function=float, + vals=Numbers(-10, 10)) + self.gates.add_parameter('chan{}step'.format(i), + get_cmd=cmdbase + '?', + set_cmd=cmdbase + ' {:.4f}', + parse_function=float, + vals=Numbers(-10, 10), + sweep_step=0.1, sweep_delay=0.005) + self.gates.add_function('reset', call_cmd='rst') + + self.source = MockInstrument('source', model=self.model, delay=0.001) + self.source.add_parameter('amplitude', get_cmd='ampl?', + set_cmd='ampl {:.4f}', parse_function=float, + vals=Numbers(0, 1), + sweep_step=0.2, sweep_delay=0.005) + + self.meter = MockInstrument('meter', model=self.model, delay=0.001, + read_response=self.read_response) + self.meter.add_parameter('amplitude', get_cmd='ampl?', + parse_function=float) + self.meter.add_function('echo', call_cmd='echo {:.2f}?', + parameters=[Numbers(0, 1000)], + parse_function=float) + + self.init_ts = datetime.now().strftime('%Y-%m-%d %H:%M:%S') + + def check_ts(self, ts_str): + now = datetime.now().strftime('%Y-%m-%d %H:%M:%S') + self.assertTrue(self.init_ts <= ts_str <= now) + + def test_mock_instrument(self): + gates, source, meter = self.gates, self.source, self.meter + + # initial state + # short form of getter + self.assertEqual(meter.get('amplitude'), 0) + # shortcut to the parameter, longer form of get + self.assertEqual(meter['amplitude'].get(), 0) + # explicit long form of getter + self.assertEqual(meter.parameters['amplitude'].get(), 0) + # both should produce the same history entry + self.assertEqual(len(meter.history), 3) + self.assertEqual(meter.history[0][1:], ('ask', 'ampl')) + self.assertEqual(meter.history[0][1:], ('ask', 'ampl')) + + # errors trying to set (or validate) invalid param values + # put here so we ensure that these errors don't make it to + # the history (ie they don't result in hardware commands) + with self.assertRaises(ValueError): + gates.set('chan1', '1') + with self.assertRaises(ValueError): + gates.parameters['chan1'].validate('1') + + # change one param at a time + gates.set('chan0', 0.5) + self.assertEqual(gates.get('chan0'), 0.5) + self.assertEqual(meter.get('amplitude'), 0.05) + + gates.set('chan1', 2) + self.assertEqual(gates.get('chan1'), 2) + self.assertEqual(meter.get('amplitude'), 0.45) + + gates.set('chan2', -3.2) + self.assertEqual(gates.get('chan2'), -3.2) + self.assertEqual(meter.get('amplitude'), -2.827) + + source.set('amplitude', 0.6) + self.assertEqual(source.get('amplitude'), 0.6) + self.assertEqual(meter.get('amplitude'), -16.961) + + # check just the size and timestamps of histories + for entry in gates.history + source.history + meter.history: + self.check_ts(entry[0]) + self.assertEqual(len(gates.history), 6) + self.assertEqual(len(meter.history), 7) + self.assertEqual(len(source.history), 5) + + # plus enough setters to check the parameter sweep + # first source has to get the starting value + self.assertEqual(source.history[0][1:], ('ask', 'ampl')) + # then it writes each + self.assertEqual(source.history[1][1:], ('write', 'ampl', '0.3000')) + self.assertEqual(source.history[2][1:], ('write', 'ampl', '0.5000')) + self.assertEqual(source.history[3][1:], ('write', 'ampl', '0.6000')) + + # test sync/async - so far all calls have been sync, even though gates + # was defined as async. Mock some async calls to test other conversions + wait_for_async(source.set_async, 'amplitude', 0.8) + self.assertEqual(wait_for_async(source.get_async, 'amplitude'), 0.8) + wait_for_async(gates.set_async, 'chan1', -2) + self.assertEqual(wait_for_async(gates.get_async, 'chan1'), -2) + + # test functions + self.assertEqual(meter.call('echo', 1.2345), 1.23) # model returns .2f + with self.assertRaises(TypeError): + meter.call('echo', 1, 2) + with self.assertRaises(ValueError): + meter.call('echo', '1') + + # validating before actually trying to call + with self.assertRaises(TypeError): + meter.functions['echo'].validate(1, 2) + with self.assertRaises(ValueError): + meter.functions['echo'].validate('1') + gates.call('reset') + self.assertEqual(gates.get('chan0'), 0) + + # and async functions + self.assertEqual(wait_for_async(meter.call_async, 'echo', 4.567), 4.57) + gates.set('chan0', 1) + self.assertEqual(gates.get('chan0'), 1) + wait_for_async(gates.call_async, 'reset') + self.assertEqual(gates.get('chan0'), 0) + + def test_mock_async_set_sweep(self): + gates = self.gates + wait_for_async(gates.set_async, 'chan0step', 0.5) + self.assertEqual(len(gates.history), 6) + self.assertEqual( + [float(h[3]) for h in gates.history if h[1] == 'write'], + [0.1, 0.2, 0.3, 0.4, 0.5]) + + def test_mock_instrument_errors(self): + gates, meter = self.gates, self.meter + with self.assertRaises(ValueError): + gates.ask('no question') + with self.assertRaises(ValueError): + gates.ask('question?yes but more after') + + with self.assertRaises(ModelError): + gates.write('ampl 1') + with self.assertRaises(ModelError): + gates.ask('ampl?') + + with self.assertRaises(TypeError): + MockInstrument('', delay='forever') + with self.assertRaises(TypeError): + MockInstrument('', delay=-1) + + with self.assertRaises(AttributeError): + MockInstrument('', model=None) + + with self.assertRaises(KeyError): + gates.add_parameter('chan0', get_cmd='boo') + with self.assertRaises(KeyError): + gates.add_function('reset', call_cmd='hoo') + + with self.assertRaises(NotImplementedError): + meter.set('amplitude', 0.5) + meter.add_parameter('gain', set_cmd='gain {:.3f}') + with self.assertRaises(NotImplementedError): + meter.get('gain') + + with self.assertRaises(TypeError): + gates.add_parameter('fugacity', set_cmd='f {:.4f}', vals=[1, 2, 3]) + + def test_sweep_steps_edge_case(self): + # MultiType with sweeping is weird - not sure why one would do this, + # but we should handle it + source = self.source + source.add_parameter('amplitude2', get_cmd='ampl?', + set_cmd='ampl {}', parse_function=float, + vals=MultiType(Numbers(0, 1), Strings()), + sweep_step=0.2, sweep_delay=0.005) + self.assertEqual(len(source.history), 0) + source.set('amplitude2', 'Off') + self.assertEqual(len(source.history), 2) # get then set + source.set('amplitude2', 0.2) + self.assertEqual(len(source.history), 3) # single set + source.set('amplitude2', 0.8) # num -> num is the only real sweep + self.assertEqual(len(source.history), 6) # 3-step sweep + source.set('amplitude2', 'Off') + self.assertEqual(len(source.history), 7) # single set + + def test_set_sweep_errors(self): + gates = self.gates + + # for reference, some add_parameter's that should work + gates.add_parameter('t0', set_cmd='{}', vals=Numbers(), + sweep_step=0.1, sweep_delay=0.01) + gates.add_parameter('t2', set_cmd='{}', vals=Ints(), + sweep_step=1, sweep_delay=0.01, + max_val_age=0) + + with self.assertRaises(TypeError): + # can't sweep non-numerics + gates.add_parameter('t1', set_cmd='{}', vals=Strings(), + sweep_step=1, sweep_delay=0.01) + with self.assertRaises(TypeError): + # need a numeric step too + gates.add_parameter('t1', set_cmd='{}', vals=Numbers(), + sweep_step='a skosh', sweep_delay=0.01) + with self.assertRaises(TypeError): + # Ints requires and int step + gates.add_parameter('t1', set_cmd='{}', vals=Ints(), + sweep_step=0.1, sweep_delay=0.01) + with self.assertRaises(ValueError): + # need a positive step + gates.add_parameter('t1', set_cmd='{}', vals=Numbers(), + sweep_step=0, sweep_delay=0.01) + with self.assertRaises(ValueError): + # need a positive step + gates.add_parameter('t1', set_cmd='{}', vals=Numbers(), + sweep_step=-0.1, sweep_delay=0.01) + with self.assertRaises(TypeError): + # need a numeric delay + gates.add_parameter('t1', set_cmd='{}', vals=Numbers(), + sweep_step=0.1, sweep_delay='a tad') + with self.assertRaises(ValueError): + # need a positive delay + gates.add_parameter('t1', set_cmd='{}', vals=Numbers(), + sweep_step=0.1, sweep_delay=-0.01) + with self.assertRaises(ValueError): + # need a positive delay + gates.add_parameter('t1', set_cmd='{}', vals=Numbers(), + sweep_step=0.1, sweep_delay=0) + with self.assertRaises(TypeError): + # need a numeric max_val_age + gates.add_parameter('t1', set_cmd='{}', vals=Numbers(), + sweep_step=0.1, sweep_delay=0.01, + max_val_age='an hour') + with self.assertRaises(ValueError): + # need a non-negative max_val_age + gates.add_parameter('t1', set_cmd='{}', vals=Numbers(), + sweep_step=0.1, sweep_delay=0.01, + max_val_age=-1) + + def test_snapshot(self): + self.assertEqual(self.meter.snapshot(), { + 'parameters': {'amplitude': {}}, + 'functions': {'echo': {}} + }) + + amp = self.meter.get('amplitude') + ampsnap = self.meter.snapshot()['parameters']['amplitude'] + self.assertEqual(ampsnap['value'], amp) + amp_ts = datetime.strptime(ampsnap['ts'], '%Y-%m-%d %H:%M:%S') + self.assertLessEqual(amp_ts, datetime.now()) + self.assertGreater(amp_ts, datetime.now() - timedelta(seconds=1.1)) + + def test_mock_read(self): + gates, meter = self.gates, self.meter + self.assertEqual(meter.read(), self.read_response) + self.assertEqual(wait_for_async(meter.read_async), + self.read_response) + self.assertEqual(gates.read(), self.read_response) + self.assertEqual(wait_for_async(gates.read_async), + self.read_response) + + def test_base_instrument_errors(self): + b = BaseInstrument('silent') + + with self.assertRaises(NotImplementedError): + b.read() + with self.assertRaises(NotImplementedError): + b.write('hello!') + with self.assertRaises(NotImplementedError): + b.ask('how are you?') + + with self.assertRaises(NotImplementedError): + wait_for_async(b.read_async) + with self.assertRaises(NotImplementedError): + wait_for_async(b.write_async, 'goodbye') + with self.assertRaises(NotImplementedError): + wait_for_async(b.ask_async, 'are we having fun yet?') + + with self.assertRaises(TypeError): + b.add_function('skip', call_cmd='skip {}', + parameters=['not a validator']) + with self.assertRaises(NoCommandError): + b.add_function('jump') + with self.assertRaises(NoCommandError): + b.add_parameter('height') + + def test_sweep_values_errors(self): + gates, source, meter = self.gates, self.source, self.meter + c0 = gates.parameters['chan0'] + source_amp = source.parameters['amplitude'] + meter_amp = meter.parameters['amplitude'] + + # only complete 3-part slices are valid + with self.assertRaises(TypeError): + c0[1:2] # For Int params this could be defined as step=1 + with self.assertRaises(TypeError): + c0[:2:3] + with self.assertRaises(TypeError): + c0[1::3] + with self.assertRaises(TypeError): + c0[:] # For Enum params we *could* define this one too... + + # fails if the parameter has no setter + # with self.assertRaises(AttributeError): + meter_amp[0] + + # validates every step value against the parameter's Validator + with self.assertRaises(ValueError): + c0[5:15:1] + with self.assertRaises(ValueError): + c0[5.0:15.0:1.0] + with self.assertRaises(ValueError): + c0[-12] + with self.assertRaises(ValueError): + c0[-5, 12, 5] + with self.assertRaises(ValueError): + c0[-5, 12:8:1, 5] + + # cannot combine SweepValues for different parameters + with self.assertRaises(TypeError): + c0[0.1] + source_amp[0.2] + + # improper use of extend + with self.assertRaises(TypeError): + c0[0.1].extend(5) + + # SweepValue object has no getter, even if the parameter does + with self.assertRaises(AttributeError): + c0[0.1].get + with self.assertRaises(AttributeError): + c0[0.1].get_async + + def test_sweep_values_valid(self): + gates = self.gates + c0 = gates.parameters['chan0'] + c1_noasync = gates.parameters['chan1'] + del c1_noasync.set_async + with self.assertRaises(AttributeError): + c1_noasync.set_async + c2_nosync = gates.parameters['chan2'] + del c2_nosync.set + with self.assertRaises(AttributeError): + c2_nosync.set + + c0_sv = c0[1] + c1_sv = c1_noasync[1] + c2_sv = c2_nosync[1] + # setters get mapped + self.assertEqual(c0_sv.set, c0.set) + self.assertEqual(c0_sv.set_async, c0.set_async) + self.assertEqual(c1_sv.set, c1_noasync.set) + self.assertTrue(asyncio.iscoroutinefunction(c1_sv.set_async)) + self.assertEqual(c2_sv.set_async, c2_nosync.set_async) + self.assertTrue(callable(c2_sv.set)) + # normal sequence operations access values + self.assertEqual(list(c0_sv), [1]) + self.assertEqual(c0_sv[0], 1) + self.assertTrue(1 in c0_sv) + self.assertFalse(2 in c0_sv) + + # in-place and copying addition + c0_sv += c0[1.5:1.8:0.1] + c0_sv2 = c0_sv + c0[2] + self.assertEqual(list(c0_sv), [1, 1.5, 1.6, 1.7]) + self.assertEqual(list(c0_sv2), [1, 1.5, 1.6, 1.7, 2]) + + # append and extend + c0_sv3 = c0[2] + # append only works with straight values + c0_sv3.append(2.1) + # extend can use another SweepValue, (even if it only has one value) + c0_sv3.extend(c0[2.2]) + # extend can also take a sequence + c0_sv3.extend([2.3]) + # as can addition + c0_sv3 += [2.4] + c0_sv4 = c0_sv3 + [2.5, 2.6] + self.assertEqual(list(c0_sv3), [2, 2.1, 2.2, 2.3, 2.4]) + self.assertEqual(list(c0_sv4), [2, 2.1, 2.2, 2.3, 2.4, 2.5, 2.6]) + + # len + self.assertEqual(len(c0_sv3), 5) + + # in-place and copying reverse + c0_sv.reverse() + c0_sv5 = reversed(c0_sv) + self.assertEqual(list(c0_sv), [1.7, 1.6, 1.5, 1]) + self.assertEqual(list(c0_sv5), [1, 1.5, 1.6, 1.7]) + + # multi-key init, where first key is itself a list + c0_sv6 = c0[[1, 3], 4] + # copying + c0_sv7 = c0_sv6.copy() + self.assertEqual(list(c0_sv6), [1, 3, 4]) + self.assertEqual(list(c0_sv7), [1, 3, 4]) + self.assertFalse(c0_sv6 is c0_sv7) diff --git a/qcodes/tests/test_metadata.py b/qcodes/tests/test_metadata.py new file mode 100644 index 000000000000..539996945ed2 --- /dev/null +++ b/qcodes/tests/test_metadata.py @@ -0,0 +1,48 @@ +from unittest import TestCase + +from qcodes.utils.metadata import Metadatable + + +class TestMetadatable(TestCase): + def test_load(self): + m = Metadatable() + self.assertEqual(m.metadata, {}) + m.load_metadata({'something else': {1: 2, 3: 4}}) + self.assertEqual(m.metadata, {}) + m.load_metadata({'metadata': {1: 2, 3: 4}}) + self.assertEqual(m.metadata, {1: 2, 3: 4}) + m.load_metadata({'metadata': {1: 5}}) + self.assertEqual(m.metadata, {1: 5, 3: 4}) + + def test_init(self): + m = Metadatable(metadata={2: 3}, not_metadata={4: 5}) + self.assertEqual(m.metadata, {2: 3}) + + class HasSnapshotBase(Metadatable): + def snapshot_base(self): + return {'cheese': 'gruyere'} + + class HasSnapshot(Metadatable): + # Users shouldn't do this... but we'll test its behavior + # for completeness + def snapshot(self): + return {'fruit': 'kiwi'} + + def test_snapshot(self): + m = Metadatable(metadata={6: 7}) + self.assertEqual(m.snapshot_base(), {}) + self.assertEqual(m.snapshot(), {'metadata': {6: 7}}) + del m.metadata[6] + self.assertEqual(m.snapshot(), {}) + + sb = self.HasSnapshotBase(metadata={7: 8}) + self.assertEqual(sb.snapshot_base(), {'cheese': 'gruyere'}) + self.assertEqual(sb.snapshot(), + {'cheese': 'gruyere', 'metadata': {7: 8}}) + del sb.metadata[7] + self.assertEqual(sb.snapshot(), sb.snapshot_base()) + + s = self.HasSnapshot(metadata={8: 9}) + self.assertEqual(s.snapshot(), {'fruit': 'kiwi'}) + self.assertEqual(s.snapshot_base(), {}) + self.assertEqual(s.metadata, {8: 9}) diff --git a/qcodes/tests/test_sync_async.py b/qcodes/tests/test_sync_async.py new file mode 100644 index 000000000000..2a5d57927dff --- /dev/null +++ b/qcodes/tests/test_sync_async.py @@ -0,0 +1,236 @@ +import asyncio +from unittest import TestCase +from time import time +import sys + +from qcodes.utils.sync_async import (wait_for_async, mock_async, mock_sync, + syncable_command, NoCommandError) + + +@asyncio.coroutine +def async1(v): + return v**2 + + +@asyncio.coroutine +def async2(v, n): + for i in range(n): + yield from asyncio.sleep(0.001) + return v**2 + + +@asyncio.coroutine +def async3(v, n): + return (yield from async2(v, n)) + + +try: + from qcodes.tests.py35_syntax import async1_new, async2_new, async3_new + py35 = True +except: + py35 = False + print('python 3.5+ not found, only testing older async syntax', + file=sys.stderr) + + +class TestAsync(TestCase): + def test_simple(self): + self.assertEqual(wait_for_async(async1, 2), 4) + if py35: + self.assertEqual(wait_for_async(async1_new, 2), 4) + + def check_time(self, f, a, b, out, tmin, tmax): + t1 = time() + self.assertEqual(wait_for_async(f, a, b), out) + t2 = time() + self.assertGreaterEqual(t2 - t1, tmin) + # measure of how good async timing is + # answer: about a fraction of a millisecond, and always + # longer than specified, never shorter + # TODO: make some benchmarks so we can understand this on + # different systems where it's deployed + self.assertLess(t2 - t1, tmax) + + def test_await(self): + self.check_time(async2, 3, 100, 9, 0.1, 0.2) + if py35: + self.check_time(async2_new, 3, 100, 9, 0.1, 0.2) + + def test_chain(self): + self.check_time(async3, 5, 100, 25, 0.1, 0.2) + if py35: + self.check_time(async3_new, 5, 100, 25, 0.1, 0.2) + + def test_mock_async(self): + def sync_no_args(): + return 42 + + def sync_args(a=1, b=2, c=3): + return a + b + c + + # we can't wait on a sync function + with self.assertRaises(TypeError): + self.assertEqual(wait_for_async(sync_no_args), 12) + + # but with mock_async we can + # also tests argument passing in wait_for_async + self.assertEqual(wait_for_async(mock_async(sync_no_args)), 42) + self.assertEqual(wait_for_async(mock_async(sync_args)), 6) + self.assertEqual(wait_for_async(mock_async(sync_args), 10), 15) + self.assertEqual(wait_for_async(mock_async(sync_args), 0, 0), 3) + self.assertEqual(wait_for_async(mock_async(sync_args), 2, 3, 4), 9) + self.assertEqual(wait_for_async(mock_async(sync_args), b=10), 14) + self.assertEqual(wait_for_async(mock_async(sync_args), 5, c=20), 27) + + with self.assertRaises(TypeError): + wait_for_async(mock_async(sync_no_args), 0) + with self.assertRaises(TypeError): + wait_for_async(mock_async(sync_no_args), a=0) + + def test_mock_sync(self): + f1 = mock_sync(async1) + f2 = mock_sync(async2) + f3 = mock_sync(async3) + + self.assertEqual(f1(6), 36) + self.assertEqual(f2(7, n=10), 49) + self.assertEqual(f3(8, 5), 64) + + if py35: + f1 = mock_sync(async1_new) + f2 = mock_sync(async2_new) + f3 = mock_sync(async3_new) + + self.assertEqual(f1(6), 36) + self.assertEqual(f2(7, n=10), 49) + self.assertEqual(f3(8, 5), 64) + + def test_reentrant_wait_for_async(self): + f = mock_sync(mock_async(mock_sync(async1))) + self.assertEqual(f(9), 81) + + if py35: + f = mock_sync(mock_async(mock_sync(async1_new))) + self.assertEqual(f(9), 81) + + +class CustomError(Exception): + pass + + +class TestSyncableCommand(TestCase): + def test_bad_calls(self): + with self.assertRaises(TypeError): + syncable_command() + + with self.assertRaises(TypeError): + syncable_command(cmd='') + + with self.assertRaises(TypeError): + syncable_command(0, '', parse_function=lambda: 1) + + with self.assertRaises(TypeError): + syncable_command(0, cmd='', exec_str='not a function') + + with self.assertRaises(TypeError): + syncable_command(0, cmd='', aexec_str='also not a function') + + def test_no_cmd(self): + with self.assertRaises(NoCommandError): + syncable_command(0) + + def no_cmd_function(): + raise CustomError('no command') + + no_cmd, no_acmd = syncable_command(0, no_cmd_function=no_cmd_function) + with self.assertRaises(CustomError): + no_cmd() + with self.assertRaises(CustomError): + wait_for_async(no_acmd) + + def test_cmd_str(self): + def f_now(x): + return x + ' now' + + @asyncio.coroutine + def adelay(x): + yield from asyncio.sleep(0.001) + return x + ' delayed' + + def upper(s): + return s.upper() + + # only sync exec_str + cmd, acmd = syncable_command(0, 'pickles', exec_str=f_now) + self.assertEqual(cmd(), 'pickles now') + self.assertEqual(wait_for_async(acmd), 'pickles now') + + # only async exec_str + cmd, acmd = syncable_command(0, 'lemons', aexec_str=adelay) + self.assertEqual(cmd(), 'lemons delayed') + self.assertEqual(wait_for_async(acmd), 'lemons delayed') + + # separate sync and async exec_str + cmd, acmd = syncable_command(0, 'herring', exec_str=f_now, + aexec_str=adelay) + self.assertEqual(cmd(), 'herring now') + self.assertEqual(wait_for_async(acmd), 'herring delayed') + with self.assertRaises(TypeError): + cmd(12) + with self.assertRaises(TypeError): + wait_for_async(acmd, 21) + + # separate sync/async with parsing + cmd, acmd = syncable_command(0, 'blue', exec_str=f_now, + aexec_str=adelay, parse_function=upper) + self.assertEqual(cmd(), 'BLUE NOW') + self.assertEqual(wait_for_async(acmd), 'BLUE DELAYED') + + # parameter insertion + cmd, acmd = syncable_command(3, '{} is {:.2f}% better than {}', + exec_str=f_now, aexec_str=adelay) + self.assertEqual(cmd('ice cream', 56.2, 'cake'), + 'ice cream is 56.20% better than cake now') + self.assertEqual(wait_for_async(acmd, 'cheese', 30, 'cheese'), + 'cheese is 30.00% better than cheese delayed') + with self.assertRaises(ValueError): + cmd('cake', 'a whole lot', 'pie') + + with self.assertRaises(TypeError): + cmd('donuts', 100, 'bagels', 'with cream cheese') + + def test_cmd_function(self): + def myexp(a, b): + return a ** b + + @asyncio.coroutine + def mymod(a, b): + yield from asyncio.sleep(0.001) + return a % b + + # only sync + cmd, acmd = syncable_command(2, myexp) + self.assertEqual(cmd(10, 3), 1000) + self.assertEqual(wait_for_async(acmd, 2, 10), 1024) + with self.assertRaises(TypeError): + syncable_command(3, myexp) + with self.assertRaises(TypeError): + syncable_command(2, mymod) + + # only async + cmd, acmd = syncable_command(2, None, mymod) + self.assertEqual(cmd(10, 3), 1) + self.assertEqual(wait_for_async(acmd, 2, 10), 2) + with self.assertRaises(TypeError): + syncable_command(3, None, mymod) + with self.assertRaises(TypeError): + syncable_command(2, None, myexp) + + # both sync and async + cmd, acmd = syncable_command(2, myexp, mymod) + self.assertEqual(cmd(10, 3), 1000) + self.assertEqual(wait_for_async(acmd, 10, 3), 1) + with self.assertRaises(TypeError): + cmd(1, 2, 3) + with self.assertRaises(TypeError): + wait_for_async(acmd, 4, 5, 6) diff --git a/qcodes/tests/test_validators.py b/qcodes/tests/test_validators.py new file mode 100644 index 000000000000..89575f8c3491 --- /dev/null +++ b/qcodes/tests/test_validators.py @@ -0,0 +1,313 @@ +from unittest import TestCase +import math + +from qcodes.utils.validators import (Validator, Anything, Strings, Numbers, + Ints, Enum, MultiType) + + +class AClass(object): + def method_a(self): + raise RuntimeError('function should not get called') + + +def a_func(): + pass + + +class TestBaseClass(TestCase): + def test_instantiate(self): + # you cannot instantiate the base class + with self.assertRaises(NotImplementedError): + Validator() + + class BrokenValidator(Validator): + def __init__(self): + pass + + def test_broken(self): + # nor can you call is_valid without overriding it in a subclass + b = self.BrokenValidator() + with self.assertRaises(NotImplementedError): + b.is_valid(0) + + +class TestAnything(TestCase): + def test_real_anything(self): + a = Anything() + for v in [None, 0, 1, 0.0, 1.2, '', 'hi!', [1, 2, 3], [], + {'a': 1, 'b': 2}, {}, set([1, 2, 3]), a, range(10), + True, False, float("nan"), float("inf"), b'good', + AClass, AClass(), a_func]: + self.assertTrue(a.is_valid(v)) + + self.assertEqual(repr(a), '') + + def test_failed_anything(self): + with self.assertRaises(TypeError): + Anything(1) + + with self.assertRaises(TypeError): + Anything(values=[1, 2, 3]) + + +class TestStrings(TestCase): + long_string = '+'.join(str(i) for i in range(100000)) + danish = '\u00d8rsted F\u00e6lled' + chinese = '\u590f\u65e5\u7545\u9500\u699c\u5927\u724c\u7f8e' + + strings = ['', '0', '10' '1.0e+10', 'a', 'Ja', 'Artichokes!', + danish, chinese, long_string] + + not_strings = [0, 1, 1.0e+10, bytes('', 'utf8'), + bytes(danish, 'utf8'), bytes(chinese, 'utf8'), + [], [1, 2, 3], {}, {'a': 1, 'b': 2}, + True, False, None, AClass, AClass(), a_func] + + def test_unlimited(self): + s = Strings() + + for v in self.strings: + self.assertTrue(s.is_valid(v)) + + for v in self.not_strings: + self.assertFalse(s.is_valid(v)) + + self.assertEqual(repr(s), '') + + def test_min(self): + for min_len in [0, 1, 5, 10, 100]: + s = Strings(min_length=min_len) + for v in self.strings: + self.assertEqual(s.is_valid(v), len(v) >= min_len) + + for v in self.not_strings: + self.assertFalse(s.is_valid(v)) + + self.assertEqual(repr(s), '=100>') + + def test_max(self): + for max_len in [1, 5, 10, 100]: + s = Strings(max_length=max_len) + for v in self.strings: + self.assertEqual(s.is_valid(v), len(v) <= max_len) + + for v in self.not_strings: + self.assertFalse(s.is_valid(v)) + + self.assertEqual(repr(s), '') + + def test_range(self): + s = Strings(1, 10) + + for v in self.strings: + self.assertEqual(s.is_valid(v), 1 <= len(v) <= 10) + + for v in self.not_strings: + self.assertFalse(s.is_valid(v)) + + self.assertEqual(repr(s), '') + + # single-valued range + self.assertEqual(repr(Strings(10, 10)), '') + + def test_failed_strings(self): + with self.assertRaises(TypeError): + Strings(1, 2, 3) + + with self.assertRaises(TypeError): + Strings(10, 9) + + with self.assertRaises(TypeError): + Strings(max_length=0) + + with self.assertRaises(TypeError): + Strings(min_length=1e12) + + for length in [-1, 3.5, '2', None]: + with self.assertRaises(TypeError): + Strings(max_length=length) + + with self.assertRaises(TypeError): + Strings(min_length=length) + + +class TestNumbers(TestCase): + numbers = [0, 1, -1, 0.1, -0.1, 100, 1000000, 1.0, 3.5, -2.3e6, 5.5e15, + 1.34e-10, -2.5e-5, math.pi, math.e, + # warning: True==1 and False==0 + True, False, + # warning: +/- inf are allowed if max & min are not specified! + -float("inf"), float("inf")] + not_numbers = ['', None, float("nan"), '1', [], {}, [1, 2], {1: 1}, b'good', + AClass, AClass(), a_func] + + def test_unlimited(self): + n = Numbers() + + for v in self.numbers: + self.assertTrue(n.is_valid(v)) + + for v in self.not_numbers: + self.assertFalse(n.is_valid(v)) + + def test_min(self): + for min_val in [-1e20, -1, -0.1, 0, 0.1, 10]: + n = Numbers(min_value=min_val) + for v in self.numbers: + self.assertEqual(n.is_valid(v), v >= min_val) + + for v in self.not_numbers: + self.assertFalse(n.is_valid(v)) + + def test_max(self): + for max_val in [-1e20, -1, -0.1, 0, 0.1, 10]: + n = Numbers(max_value=max_val) + for v in self.numbers: + self.assertEqual(n.is_valid(v), v <= max_val) + + for v in self.not_numbers: + self.assertFalse(n.is_valid(v)) + + def test_range(self): + n = Numbers(0.1, 3.5) + + for v in self.numbers: + self.assertEqual(n.is_valid(v), 0.1 <= v <= 3.5) + + for v in self.not_numbers: + self.assertFalse(n.is_valid(v)) + + self.assertEqual(repr(n), '') + + def test_failed_numbers(self): + with self.assertRaises(TypeError): + Numbers(1, 2, 3) + + with self.assertRaises(TypeError): + Numbers(1, 1) # min >= max + + for val in self.not_numbers: + with self.assertRaises(TypeError): + Numbers(max_value=val) + + with self.assertRaises(TypeError): + Numbers(min_value=val) + + +class TestInts(TestCase): + ints = [0, 1, 10, -1, 100, 1000000, int(-1e15), int(1e15), + # warning: True==1 and False==0 - we *could* prohibit these, using + # isinstance(v, bool) + True, False] + not_ints = [0.1, -0.1, 1.0, 3.5, -2.3e6, 5.5e15, 1.34e-10, -2.5e-5, + math.pi, math.e, '', None, float("nan"), float("inf"), -float("inf"), '1', + [], {}, [1, 2], {1: 1}, b'good', AClass, AClass(), a_func] + + def test_unlimited(self): + n = Ints() + + for v in self.ints: + self.assertTrue(n.is_valid(v)) + + for v in self.not_ints: + self.assertFalse(n.is_valid(v)) + + def test_min(self): + for min_val in self.ints: + n = Ints(min_value=min_val) + for v in self.ints: + self.assertEqual(n.is_valid(v), v >= min_val) + + for v in self.not_ints: + self.assertFalse(n.is_valid(v)) + + def test_max(self): + for max_val in self.ints: + n = Ints(max_value=max_val) + for v in self.ints: + self.assertEqual(n.is_valid(v), v <= max_val) + + for v in self.not_ints: + self.assertFalse(n.is_valid(v)) + + def test_range(self): + n = Ints(0, 10) + + for v in self.ints: + self.assertEqual(n.is_valid(v), 0 <= v <= 10) + + for v in self.not_ints: + self.assertFalse(n.is_valid(v)) + + self.assertEqual(repr(n), '') + self.assertTrue(n.is_numeric) + + def test_failed_numbers(self): + with self.assertRaises(TypeError): + Ints(1, 2, 3) + + with self.assertRaises(TypeError): + Ints(1, 1) # min >= max + + for val in self.not_ints: + with self.assertRaises((TypeError, OverflowError)): + Ints(max_value=val) + + with self.assertRaises((TypeError, OverflowError)): + Ints(min_value=val) + + +class TestEnum(TestCase): + enums = [ + [True, False], + [1, 2, 3], + [True, None, 1, 2.3, 'Hi!', b'free', (1, 2), float("inf")] + ] + + # enum items must be immutable - tuple OK, list bad. + not_enums = [[], [[1, 2], [3, 4]]] + + def test_good(self): + for enum in self.enums: + e = Enum(*enum) + + for v in enum: + self.assertTrue(e.is_valid(v)) + + for v in [22, 'bad data', [44, 55]]: + self.assertFalse(e.is_valid(v)) + + self.assertEqual(repr(e), ''.format(repr(set(enum)))) + + # Enum is never numeric, even if its members are all numbers + # because the use of is_numeric is for sweeping + self.assertFalse(e.is_numeric) + + def test_bad(self): + for enum in self.not_enums: + with self.assertRaises(TypeError): + Enum(*enum) + + +class TestMultiType(TestCase): + def test_good(self): + m = MultiType(Strings(2, 4), Ints(10, 1000)) + + for v in [10, 11, 123, 1000, 'aa', 'mop', 'FRED']: + self.assertTrue(m.is_valid(v)) + + for v in [9, 1001, 'Q', 'Qcode', None, 100.0, b'nice', [], {}, + a_func, AClass, AClass(), True, False]: + self.assertFalse(m.is_valid(v)) + + self.assertEqual( + repr(m), '') + + self.assertTrue(m.is_numeric) + + self.assertFalse(MultiType(Strings(), Enum(1, 2)).is_numeric) + + def test_bad(self): + for args in [[], [1], [Strings(), True]]: + with self.assertRaises(TypeError): + MultiType(*args) diff --git a/qcodes/utils/__init__.py b/qcodes/utils/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/qcodes/utils/helpers.py b/qcodes/utils/helpers.py new file mode 100644 index 000000000000..bed0089d69f2 --- /dev/null +++ b/qcodes/utils/helpers.py @@ -0,0 +1,86 @@ +from asyncio import iscoroutinefunction +from inspect import getargspec, ismethod +from collections import Iterable +import math +import logging +from datetime import datetime + + +def is_sequence(obj): + ''' + is an object a sequence? We do not consider strings to be sequences, + but note that mappings (dicts) and unordered sequences (sets) ARE + sequences by this definition. + ''' + return isinstance(obj, Iterable) and not isinstance(obj, (str, bytes)) + + +def is_function(f, arg_count, coroutine=False): + ''' + require a function with the specified number of positional arguments + (and no kwargs) which either is or is not a coroutine + type casting "functions" are allowed, but only in the 1-argument form + ''' + if not isinstance(arg_count, int) or arg_count < 0: + raise TypeError('arg_count must be a non-negative integer') + + if not (callable(f) and bool(coroutine) is iscoroutinefunction(f)): + return False + + if isinstance(f, type): + # for type casting functions, eg int, str, float + # only support the one-parameter form of these, + # otherwise the user should make an explicit function. + return arg_count == 1 + + argspec = getargspec(f) + if argspec.varargs: + # we can't check the arg count if there's a *args parameter + # so you're on your own at that point + # unfortunately, the asyncio.coroutine decorator wraps with + # *args and **kw so we can't count arguments with the old async + # syntax, only the new syntax. + return True + + # getargspec includes 'self' in the arg count, even though + # it's not part of calling the function. So take it out. + return len(argspec.args) - ismethod(f) == arg_count + + +# could use numpy.arange here, but +# a) we don't want to require that as a dep so low level +# b) I'd like to be more flexible with the sign of step +def permissive_range(start, stop, step): + ''' + returns range (as a list of values) with floating point step + + inputs: + start, stop, step + + always starts at start and moves toward stop, + regardless of the sign of step + ''' + signed_step = abs(step) * (1 if stop > start else -1) + # take off a tiny bit for rounding errors + step_count = math.ceil((stop - start) / signed_step - 1e-10) + return [start + i * signed_step for i in range(step_count)] + + +def wait_secs(finish_datetime): + delay = (finish_datetime - datetime.now()).total_seconds() + if delay < 0: + logging.warning('negative delay {} sec'.format(delay)) + return 0 + return delay + + +def make_unique(s, existing): + n = 1 + s_out = s + existing = set(existing) + + while s_out in existing: + n += 1 + s_out = '{}_{}'.format(s, n) + + return s_out diff --git a/qcodes/utils/metadata.py b/qcodes/utils/metadata.py new file mode 100644 index 000000000000..b4ee9f1f3525 --- /dev/null +++ b/qcodes/utils/metadata.py @@ -0,0 +1,27 @@ +class Metadatable(object): + def __init__(self, *args, **kwargs): + self.metadata = {} + self.load_metadata(kwargs) + + def load_metadata(self, attributes): + self.metadata.update(attributes.get('metadata', {})) + + def snapshot(self): + ''' + decorate a snapshot dictionary with metadata + DO NOT override this method if you want metadata in the snapshot + instead, override snapshot_base + ''' + + snap = self.snapshot_base() + + if len(self.metadata): + snap['metadata'] = self.metadata + + return snap + + def snapshot_base(self): + ''' + override this with the primary information for a subclass + ''' + return {} diff --git a/qcodes/utils/sync_async.py b/qcodes/utils/sync_async.py new file mode 100644 index 000000000000..d5ec6775efa1 --- /dev/null +++ b/qcodes/utils/sync_async.py @@ -0,0 +1,209 @@ +import asyncio + +from qcodes.utils.helpers import is_function + + +def wait_for_async(f, *args, **kwargs): + ''' + make an coroutine f block until it's totally finished + effectively turning it into a synchronous function + ''' + parent_loop = loop = asyncio.get_event_loop() + + # if we're already running an event loop, we need to make + # a new one to run this coroutine, because the outer one + # is already blocked + # + # this can happen if an async routine calls a sync routine + # which calls another async routine. We should try to avoid this + # but as long as there is a mixture of sync and async drivers + # it'll still happen from time to time. + nested = loop.is_running() + if nested: + loop = asyncio.new_event_loop() + asyncio.set_event_loop(loop) + + out = loop.run_until_complete(f(*args, **kwargs)) + + if nested: + asyncio.set_event_loop(parent_loop) + + return out + + +class mock_sync(object): + ''' + make a coroutine into a synchronous function + written as a callable object rather than a closure + so it's picklable on Windows + ''' + def __init__(self, f): + self._f = f + + def __call__(self, *args, **kwargs): + return wait_for_async(self._f, *args, **kwargs) + + +class mock_async(object): + ''' + make a synchronous function f awaitable + written as a callable object rather than a closure + so it's picklable on Windows + ''' + + # feels like a hack, but _is_coroutine is what + # asyncio.iscoroutinefunction looks for, and it won't find it + # in the decorator for __call__. Note that there's also + # inspect.iscoroutinefunction and inspect.isfunction but + # these do not treat objects with __call__ as functions at all + # so will always fail on mock_sync and mock_async written as + # callable objects + _is_coroutine = True + + def __init__(self, f): + self._f = f + + @asyncio.coroutine + def __call__(self, *args, **kwargs): + return self._f(*args, **kwargs) + + +class NoCommandError(Exception): + pass + + +def syncable_command(param_count, cmd=None, acmd=None, + exec_str=None, aexec_str=None, parse_function=None, + no_cmd_function=None): + ''' + create synchronous and asynchronous versions of a command + inputs: + param_count: the number of parameters to the command + cmd: the command to execute. May be: + - a string (with positional fields to .format, "{}" or "{0}" etc) + - a function (with positional parameter count matching param_count) + - None, if only an acmd is provided + acmd: an async function (coroutine) to execute + Must have positional parameter count matching param_count + + The next three inputs are only valid if cmd is a string: + exec_str: a function of one parameter to execute the command string + aexec_str: a coroutine of one parameter to execute the command string + parse_function: a function to transform the return value of the command + + the last input is how to handle missing commands + no_cmd_function: don't throw an error on definition if no command found + instead, call this function when the command is invoked, which + probably should thow an error of its own (ie NotImplementedError) + + return: + 2-tuple (call, acall) + call is a function (that takes param_count arguments) + acall is a coroutine + ''' + return _SyncableCommand(param_count, cmd, acmd, exec_str, aexec_str, + parse_function, no_cmd_function + ).out() + + +class _SyncableCommand(object): + def __init__(self, param_count, cmd, acmd, exec_str, aexec_str, + parse_function, no_cmd_function): + self.param_count = param_count + self.cmd = cmd + self.acmd = acmd + self.exec_str = exec_str + self.aexec_str = aexec_str + self.parse_function = parse_function + self.no_cmd_function = no_cmd_function + + self.exec_function = None + self.aexec_function = None + + # wrappers that may or may not be used below in constructing call / acall + def call_by_str(self, *args): + return self.exec_str(self.cmd.format(*args)) + + @asyncio.coroutine + def acall_by_str(self, *args): + return (yield from self.aexec_str(self.cmd.format(*args))) + + def call_by_str_parsed(self, *args): + return self.parse_function(self.exec_str(self.cmd.format(*args))) + + @asyncio.coroutine + def acall_by_str_parsed(self, *args): + raw_value = yield from self.aexec_str(self.cmd.format(*args)) + return self.parse_function(raw_value) + + def call_sync_by_afunction(self, *args): + return wait_for_async(self.aexec_function, *args) + + # another layer to wrap the exec functions with parameter validation + def validate_param_count(self, args): + if len(args) != self.param_count: + raise TypeError( + 'command takes exactly {} parameters'.format(self.param_count)) + + def call(self, *args): + self.validate_param_count(args) + return self.exec_function(*args) + + @asyncio.coroutine + def acall(self, *args): + self.validate_param_count(args) + return (yield from self.aexec_function(*args)) + + def out(self): + if isinstance(self.cmd, str): + if self.parse_function is None: + parsed = False + elif is_function(self.parse_function, 1): + parsed = True + else: + raise TypeError( + 'parse_function must be a function with one arg,' + + ' not {}'.format(repr(self.parse_function))) + + if is_function(self.exec_str, 1): + self.exec_function = (self.call_by_str_parsed if parsed else + self.call_by_str) + elif self.exec_str is not None: + raise TypeError('exec_str must be a function with one arg,' + + ' not {}'.format(repr(self.exec_str))) + + if is_function(self.aexec_str, 1, coroutine=True): + self.aexec_function = (self.acall_by_str_parsed if parsed else + self.acall_by_str) + elif self.aexec_str is not None: + raise TypeError('aexec_str must be a coroutine with one arg') + elif is_function(self.cmd, self.param_count): + self.exec_function = self.cmd + elif self.cmd is not None: + raise TypeError('cmd must be a string or function with ' + + '{} parameters'.format(self.param_count)) + + if is_function(self.acmd, self.param_count, coroutine=True): + self.aexec_function = self.acmd + elif self.acmd is not None: + raise TypeError('acmd must be a coroutine with ' + + '{} parameters'.format(self.param_count)) + + # do we need to create the sync or async version from the other? + if self.exec_function is None: + if self.aexec_function is None: + # neither sync or async provided: either raise an error, + # or return a default function (which probably itself just + # raises an error when called, but need not raise an error + # on creation, ie if it's OK for this command to be absent) + if self.no_cmd_function is not None: + return (self.no_cmd_function, + mock_async(self.no_cmd_function)) + else: + raise NoCommandError( + 'not enough information to construct this command') + self.exec_function = self.call_sync_by_afunction + elif self.aexec_function is None: + self.aexec_function = mock_async(self.exec_function) + + return (self.call, self.acall) diff --git a/qcodes/utils/validators.py b/qcodes/utils/validators.py new file mode 100644 index 000000000000..2670e0c85a8f --- /dev/null +++ b/qcodes/utils/validators.py @@ -0,0 +1,196 @@ +import math + +BIGSTRING = 1000000000 +BIGINT = int(1e18) + + +def range_str(min_val, max_val, name): + if max_val is not None: + if min_val is not None: + if max_val == min_val: + return ' {}={}'.format(name, min_val) + else: + return ' {}<={}<={}'.format(min_val, name, max_val) + else: + return ' {}<={}'.format(name, max_val) + elif min_val is not None: + return ' {}>={}'.format(name, min_val) + else: + return '' + + +class Validator(object): + ''' + base class for all value validators + each should have its own constructor, and override is_valid and is_numeric + ''' + def __init__(self): + raise NotImplementedError + + def is_valid(self, value): + raise NotImplementedError + + is_numeric = False # is this a numeric type (so it can be swept)? + + +class Anything(Validator): + '''allow any value to pass''' + def __init__(self): + pass + + def is_valid(self, value): + return True + + is_numeric = True + + def __repr__(self): + return '' + + +class Strings(Validator): + ''' + requires a string + optional parameters min_length and max_length limit the allowed length + to min_length <= len(value) <= max_length + ''' + + def __init__(self, min_length=0, max_length=BIGSTRING): + if isinstance(min_length, int) and min_length >= 0: + self._min_length = min_length + else: + raise TypeError('min_length must be a non-negative integer') + + if isinstance(max_length, int) and max_length >= max(min_length, 1): + self._max_length = max_length + else: + raise TypeError('max_length must be a positive integer ' + 'no smaller than min_length') + + def is_valid(self, value): + return (isinstance(value, str) and + self._min_length <= len(value) <= self._max_length) + + def __repr__(self): + minv = self._min_length or None + maxv = self._max_length if self._max_length < BIGSTRING else None + return ''.format(range_str(minv, maxv, 'len')) + + +class Numbers(Validator): + ''' + requires a number, either int or float + optional parameters min_value and max_value enforce + min_value <= value <= max_value + ''' + + def __init__(self, min_value=-float("inf"), max_value=float("inf")): + if isinstance(min_value, (float, int)): + self._min_value = min_value + else: + raise TypeError('min_value must be a number') + + if isinstance(max_value, (float, int)) and max_value > min_value: + self._max_value = max_value + else: + raise TypeError('max_value must be a number bigger than min_value') + + def is_valid(self, value): + return (isinstance(value, (float, int)) and + self._min_value <= value <= self._max_value) + + is_numeric = True + + def __repr__(self): + minv = self._min_value if math.isfinite(self._min_value) else None + maxv = self._max_value if math.isfinite(self._max_value) else None + return ''.format(range_str(minv, maxv, 'v')) + + +class Ints(Validator): + ''' + requires an integer + optional parameters min_value and max_value enforce + min_value <= value <= max_value + ''' + + def __init__(self, min_value=-BIGINT, max_value=BIGINT): + if isinstance(min_value, int): + self._min_value = min_value + else: + raise TypeError('min_value must be an integer') + + if isinstance(max_value, int) and max_value > min_value: + self._max_value = max_value + else: + raise TypeError( + 'max_value must be an integer bigger than min_value') + + def is_valid(self, value): + return (isinstance(value, int) and + self._min_value <= value <= self._max_value) + + is_numeric = True + + def __repr__(self): + minv = self._min_value if self._min_value > -BIGINT else None + maxv = self._max_value if self._max_value < BIGINT else None + return ''.format(range_str(minv, maxv, 'v')) + + +class Enum(Validator): + ''' + requires one of a provided set of values + eg. Enum(val1, val2, val3) + ''' + + def __init__(self, *values): + if not len(values): + raise TypeError('Enum needs at least one value') + + self._values = set(values) + + def is_valid(self, value): + try: + return value in self._values + except TypeError: # in case of unhashable (mutable) type + return False + + def __repr__(self): + return ''.format(repr(self._values)) + + +class MultiType(Validator): + ''' + allow the union of several different validators + for example to allow numbers as well as "off": + MultiType(Numbers(), Enum("off")) + ''' + + def __init__(self, *validators): + if not validators: + raise TypeError('MultiType needs at least one Validator') + + for v in validators: + if not isinstance(v, Validator): + raise TypeError('each argument must be a Validator') + + if v.is_numeric: + # if ANY of the contained validators is numeric, + # the MultiType is considered numeric too. + # this could cause problems if you want to sweep + # from a non-numeric to a numeric value, so we + # need to be careful about this in the sweep code + self.is_numeric = True + + self._validators = tuple(validators) + + def is_valid(self, value): + for v in self._validators: + if v.is_valid(value): + return True + + return False + + def __repr__(self): + parts = (repr(v)[1:-1] for v in self._validators) + return ''.format(', '.join(parts)) diff --git a/test2d.csv b/test2d.csv new file mode 100644 index 000000000000..213c1f42e44b --- /dev/null +++ b/test2d.csv @@ -0,0 +1,1885 @@ +ts,i_chan1(30),i_chan0(60),chan1,chan0,amplitude +2015-10-17 01:09:58:957015,0,0,-15.0,-15.0,0.003 +2015-10-17 01:09:58:992206,0,1,-15.0,-14.5,0.004 +2015-10-17 01:09:59:029454,0,2,-15.0,-14.0,0.004 +2015-10-17 01:09:59:065906,0,3,-15.0,-13.5,0.004 +2015-10-17 01:09:59:097309,0,4,-15.0,-13.0,0.005 +2015-10-17 01:09:59:132695,0,5,-15.0,-12.5,0.005 +2015-10-17 01:09:59:169090,0,6,-15.0,-12.0,0.005 +2015-10-17 01:09:59:169090,0,6,-15.0,-12.0,0.005 +2015-10-17 01:09:59:201673,0,7,-15.0,-11.5,0.006 +2015-10-17 01:09:59:233119,0,8,-15.0,-11.0,0.006 +2015-10-17 01:09:59:265649,0,9,-15.0,-10.5,0.006 +2015-10-17 01:09:59:298476,0,10,-15.0,-10.0,0.006 +2015-10-17 01:09:59:332571,0,11,-15.0,-9.5,0.006 +2015-10-17 01:09:59:364145,0,12,-15.0,-9.0,0.006 +2015-10-17 01:09:59:397577,0,13,-15.0,-8.5,0.006 +2015-10-17 01:09:59:433248,0,14,-15.0,-8.0,0.005 +2015-10-17 01:09:59:469947,0,15,-15.0,-7.5,0.005 +2015-10-17 01:09:59:504888,0,16,-15.0,-7.0,0.005 +2015-10-17 01:09:59:537157,0,17,-15.0,-6.5,0.004 +2015-10-17 01:09:59:569664,0,18,-15.0,-6.0,0.004 +2015-10-17 01:09:59:602401,0,19,-15.0,-5.5,0.004 +2015-10-17 01:09:59:635559,0,20,-15.0,-5.0,0.003 +2015-10-17 01:09:59:670334,0,21,-15.0,-4.5,0.004 +2015-10-17 01:09:59:704129,0,22,-15.0,-4.0,0.004 +2015-10-17 01:09:59:738029,0,23,-15.0,-3.5,0.004 +2015-10-17 01:09:59:770030,0,24,-15.0,-3.0,0.005 +2015-10-17 01:09:59:802464,0,25,-15.0,-2.5,0.005 +2015-10-17 01:09:59:837990,0,26,-15.0,-2.0,0.005 +2015-10-17 01:09:59:874593,0,27,-15.0,-1.5,0.006 +2015-10-17 01:09:59:906514,0,28,-15.0,-1.0,0.006 +2015-10-17 01:09:59:943027,0,29,-15.0,-0.5,0.006 +2015-10-17 01:09:59:979427,0,30,-15.0,0.0,0.006 +2015-10-17 01:10:00:011297,0,31,-15.0,0.5,0.006 +2015-10-17 01:10:00:043819,0,32,-15.0,1.0,0.006 +2015-10-17 01:10:00:075731,0,33,-15.0,1.5,0.006 +2015-10-17 01:10:00:108232,0,34,-15.0,2.0,0.005 +2015-10-17 01:10:00:142901,0,35,-15.0,2.5,0.005 +2015-10-17 01:10:00:178896,0,36,-15.0,3.0,0.005 +2015-10-17 01:10:00:178896,0,36,-15.0,3.0,0.005 +2015-10-17 01:10:00:211000,0,37,-15.0,3.5,0.004 +2015-10-17 01:10:00:242856,0,38,-15.0,4.0,0.004 +2015-10-17 01:10:00:274957,0,39,-15.0,4.5,0.004 +2015-10-17 01:10:00:308806,0,40,-15.0,5.0,0.003 +2015-10-17 01:10:00:343708,0,41,-15.0,5.5,0.004 +2015-10-17 01:10:00:375901,0,42,-15.0,6.0,0.004 +2015-10-17 01:10:00:408622,0,43,-15.0,6.5,0.004 +2015-10-17 01:10:00:441407,0,44,-15.0,7.0,0.005 +2015-10-17 01:10:00:474298,0,45,-15.0,7.5,0.005 +2015-10-17 01:10:00:506311,0,46,-15.0,8.0,0.005 +2015-10-17 01:10:00:539122,0,47,-15.0,8.5,0.006 +2015-10-17 01:10:00:570456,0,48,-15.0,9.0,0.006 +2015-10-17 01:10:00:602624,0,49,-15.0,9.5,0.006 +2015-10-17 01:10:00:634901,0,50,-15.0,10.0,0.006 +2015-10-17 01:10:00:666939,0,51,-15.0,10.5,0.006 +2015-10-17 01:10:00:699569,0,52,-15.0,11.0,0.006 +2015-10-17 01:10:00:731200,0,53,-15.0,11.5,0.006 +2015-10-17 01:10:00:763189,0,54,-15.0,12.0,0.005 +2015-10-17 01:10:00:795134,0,55,-15.0,12.5,0.005 +2015-10-17 01:10:00:827110,0,56,-15.0,13.0,0.005 +2015-10-17 01:10:00:859293,0,57,-15.0,13.5,0.004 +2015-10-17 01:10:00:890966,0,58,-15.0,14.0,0.004 +2015-10-17 01:10:00:923171,0,59,-15.0,14.5,0.004 +2015-10-17 01:10:00:923171,0,59,-15.0,14.5,0.004 +2015-10-17 01:10:01:929327,1,0,-14.0,-15.0,0.004 +2015-10-17 01:10:01:961635,1,1,-14.0,-14.5,0.004 +2015-10-17 01:10:01:993659,1,2,-14.0,-14.0,0.005 +2015-10-17 01:10:02:027251,1,3,-14.0,-13.5,0.005 +2015-10-17 01:10:02:060430,1,4,-14.0,-13.0,0.006 +2015-10-17 01:10:02:095201,1,5,-14.0,-12.5,0.007 +2015-10-17 01:10:02:129065,1,6,-14.0,-12.0,0.007 +2015-10-17 01:10:02:163237,1,7,-14.0,-11.5,0.008 +2015-10-17 01:10:02:194062,1,8,-14.0,-11.0,0.008 +2015-10-17 01:10:02:231837,1,9,-14.0,-10.5,0.009 +2015-10-17 01:10:02:263905,1,10,-14.0,-10.0,0.009 +2015-10-17 01:10:02:295972,1,11,-14.0,-9.5,0.009 +2015-10-17 01:10:02:330544,1,12,-14.0,-9.0,0.008 +2015-10-17 01:10:02:330544,1,12,-14.0,-9.0,0.008 +2015-10-17 01:10:02:367496,1,13,-14.0,-8.5,0.008 +2015-10-17 01:10:02:402657,1,14,-14.0,-8.0,0.007 +2015-10-17 01:10:02:438798,1,15,-14.0,-7.5,0.007 +2015-10-17 01:10:02:471481,1,16,-14.0,-7.0,0.006 +2015-10-17 01:10:02:503710,1,17,-14.0,-6.5,0.005 +2015-10-17 01:10:02:535331,1,18,-14.0,-6.0,0.005 +2015-10-17 01:10:02:568964,1,19,-14.0,-5.5,0.004 +2015-10-17 01:10:02:603540,1,20,-14.0,-5.0,0.004 +2015-10-17 01:10:02:639853,1,21,-14.0,-4.5,0.004 +2015-10-17 01:10:02:673101,1,22,-14.0,-4.0,0.005 +2015-10-17 01:10:02:708986,1,23,-14.0,-3.5,0.005 +2015-10-17 01:10:02:742943,1,24,-14.0,-3.0,0.006 +2015-10-17 01:10:02:775790,1,25,-14.0,-2.5,0.007 +2015-10-17 01:10:02:807079,1,26,-14.0,-2.0,0.007 +2015-10-17 01:10:02:842966,1,27,-14.0,-1.5,0.008 +2015-10-17 01:10:02:877083,1,28,-14.0,-1.0,0.008 +2015-10-17 01:10:02:911618,1,29,-14.0,-0.5,0.009 +2015-10-17 01:10:02:946583,1,30,-14.0,0.0,0.009 +2015-10-17 01:10:02:982208,1,31,-14.0,0.5,0.009 +2015-10-17 01:10:03:015790,1,32,-14.0,1.0,0.008 +2015-10-17 01:10:03:047535,1,33,-14.0,1.5,0.008 +2015-10-17 01:10:03:082489,1,34,-14.0,2.0,0.007 +2015-10-17 01:10:03:116100,1,35,-14.0,2.5,0.007 +2015-10-17 01:10:03:149250,1,36,-14.0,3.0,0.006 +2015-10-17 01:10:03:185201,1,37,-14.0,3.5,0.005 +2015-10-17 01:10:03:216705,1,38,-14.0,4.0,0.005 +2015-10-17 01:10:03:248640,1,39,-14.0,4.5,0.004 +2015-10-17 01:10:03:280344,1,40,-14.0,5.0,0.004 +2015-10-17 01:10:03:311963,1,41,-14.0,5.5,0.004 +2015-10-17 01:10:03:344039,1,42,-14.0,6.0,0.005 +2015-10-17 01:10:03:344039,1,42,-14.0,6.0,0.005 +2015-10-17 01:10:03:375797,1,43,-14.0,6.5,0.005 +2015-10-17 01:10:03:407844,1,44,-14.0,7.0,0.006 +2015-10-17 01:10:03:445380,1,45,-14.0,7.5,0.007 +2015-10-17 01:10:03:480376,1,46,-14.0,8.0,0.007 +2015-10-17 01:10:03:512800,1,47,-14.0,8.5,0.008 +2015-10-17 01:10:03:544047,1,48,-14.0,9.0,0.008 +2015-10-17 01:10:03:580826,1,49,-14.0,9.5,0.009 +2015-10-17 01:10:03:616616,1,50,-14.0,10.0,0.009 +2015-10-17 01:10:03:647828,1,51,-14.0,10.5,0.009 +2015-10-17 01:10:03:680402,1,52,-14.0,11.0,0.008 +2015-10-17 01:10:03:712186,1,53,-14.0,11.5,0.008 +2015-10-17 01:10:03:744565,1,54,-14.0,12.0,0.007 +2015-10-17 01:10:03:776603,1,55,-14.0,12.5,0.007 +2015-10-17 01:10:03:809190,1,56,-14.0,13.0,0.006 +2015-10-17 01:10:03:844970,1,57,-14.0,13.5,0.005 +2015-10-17 01:10:03:877698,1,58,-14.0,14.0,0.005 +2015-10-17 01:10:03:909857,1,59,-14.0,14.5,0.004 +2015-10-17 01:10:03:909857,1,59,-14.0,14.5,0.004 +2015-10-17 01:10:04:913124,2,0,-13.0,-15.0,0.005 +2015-10-17 01:10:04:944442,2,1,-13.0,-14.5,0.005 +2015-10-17 01:10:04:977494,2,2,-13.0,-14.0,0.006 +2015-10-17 01:10:05:010641,2,3,-13.0,-13.5,0.007 +2015-10-17 01:10:05:045233,2,4,-13.0,-13.0,0.008 +2015-10-17 01:10:05:077902,2,5,-13.0,-12.5,0.009 +2015-10-17 01:10:05:111268,2,6,-13.0,-12.0,0.01 +2015-10-17 01:10:05:143985,2,7,-13.0,-11.5,0.012 +2015-10-17 01:10:05:180227,2,8,-13.0,-11.0,0.013 +2015-10-17 01:10:05:218126,2,9,-13.0,-10.5,0.014 +2015-10-17 01:10:05:247951,2,10,-13.0,-10.0,0.014 +2015-10-17 01:10:05:279510,2,11,-13.0,-9.5,0.014 +2015-10-17 01:10:05:313399,2,12,-13.0,-9.0,0.013 +2015-10-17 01:10:05:348674,2,13,-13.0,-8.5,0.012 +2015-10-17 01:10:05:382617,2,14,-13.0,-8.0,0.01 +2015-10-17 01:10:05:418618,2,15,-13.0,-7.5,0.009 +2015-10-17 01:10:05:451888,2,16,-13.0,-7.0,0.008 +2015-10-17 01:10:05:486068,2,17,-13.0,-6.5,0.007 +2015-10-17 01:10:05:519387,2,18,-13.0,-6.0,0.006 +2015-10-17 01:10:05:519387,2,18,-13.0,-6.0,0.006 +2015-10-17 01:10:05:553041,2,19,-13.0,-5.5,0.005 +2015-10-17 01:10:05:585407,2,20,-13.0,-5.0,0.005 +2015-10-17 01:10:05:621577,2,21,-13.0,-4.5,0.005 +2015-10-17 01:10:05:652757,2,22,-13.0,-4.0,0.006 +2015-10-17 01:10:05:686217,2,23,-13.0,-3.5,0.007 +2015-10-17 01:10:05:716964,2,24,-13.0,-3.0,0.008 +2015-10-17 01:10:05:753530,2,25,-13.0,-2.5,0.009 +2015-10-17 01:10:05:785902,2,26,-13.0,-2.0,0.01 +2015-10-17 01:10:05:818691,2,27,-13.0,-1.5,0.012 +2015-10-17 01:10:05:852545,2,28,-13.0,-1.0,0.013 +2015-10-17 01:10:05:886470,2,29,-13.0,-0.5,0.014 +2015-10-17 01:10:05:918843,2,30,-13.0,0.0,0.014 +2015-10-17 01:10:05:951844,2,31,-13.0,0.5,0.014 +2015-10-17 01:10:05:985946,2,32,-13.0,1.0,0.013 +2015-10-17 01:10:06:017293,2,33,-13.0,1.5,0.012 +2015-10-17 01:10:06:051779,2,34,-13.0,2.0,0.01 +2015-10-17 01:10:06:085896,2,35,-13.0,2.5,0.009 +2015-10-17 01:10:06:119385,2,36,-13.0,3.0,0.008 +2015-10-17 01:10:06:152880,2,37,-13.0,3.5,0.007 +2015-10-17 01:10:06:185473,2,38,-13.0,4.0,0.006 +2015-10-17 01:10:06:221766,2,39,-13.0,4.5,0.005 +2015-10-17 01:10:06:253902,2,40,-13.0,5.0,0.005 +2015-10-17 01:10:06:285692,2,41,-13.0,5.5,0.005 +2015-10-17 01:10:06:321221,2,42,-13.0,6.0,0.006 +2015-10-17 01:10:06:353085,2,43,-13.0,6.5,0.007 +2015-10-17 01:10:06:388881,2,44,-13.0,7.0,0.008 +2015-10-17 01:10:06:420422,2,45,-13.0,7.5,0.009 +2015-10-17 01:10:06:455293,2,46,-13.0,8.0,0.01 +2015-10-17 01:10:06:487596,2,47,-13.0,8.5,0.012 +2015-10-17 01:10:06:519705,2,48,-13.0,9.0,0.013 +2015-10-17 01:10:06:519705,2,48,-13.0,9.0,0.013 +2015-10-17 01:10:06:551266,2,49,-13.0,9.5,0.014 +2015-10-17 01:10:06:586097,2,50,-13.0,10.0,0.014 +2015-10-17 01:10:06:622430,2,51,-13.0,10.5,0.014 +2015-10-17 01:10:06:655767,2,52,-13.0,11.0,0.013 +2015-10-17 01:10:06:688119,2,53,-13.0,11.5,0.012 +2015-10-17 01:10:06:723555,2,54,-13.0,12.0,0.01 +2015-10-17 01:10:06:756822,2,55,-13.0,12.5,0.009 +2015-10-17 01:10:06:792857,2,56,-13.0,13.0,0.008 +2015-10-17 01:10:06:826122,2,57,-13.0,13.5,0.007 +2015-10-17 01:10:06:858006,2,58,-13.0,14.0,0.006 +2015-10-17 01:10:06:892896,2,59,-13.0,14.5,0.005 +2015-10-17 01:10:06:892896,2,59,-13.0,14.5,0.005 +2015-10-17 01:10:07:894577,3,0,-12.0,-15.0,0.005 +2015-10-17 01:10:07:928164,3,1,-12.0,-14.5,0.006 +2015-10-17 01:10:07:959805,3,2,-12.0,-14.0,0.007 +2015-10-17 01:10:07:992819,3,3,-12.0,-13.5,0.009 +2015-10-17 01:10:08:024059,3,4,-12.0,-13.0,0.01 +2015-10-17 01:10:08:056031,3,5,-12.0,-12.5,0.013 +2015-10-17 01:10:08:088753,3,6,-12.0,-12.0,0.016 +2015-10-17 01:10:08:119560,3,7,-12.0,-11.5,0.019 +2015-10-17 01:10:08:151849,3,8,-12.0,-11.0,0.023 +2015-10-17 01:10:08:189080,3,9,-12.0,-10.5,0.026 +2015-10-17 01:10:08:220503,3,10,-12.0,-10.0,0.027 +2015-10-17 01:10:08:254779,3,11,-12.0,-9.5,0.026 +2015-10-17 01:10:08:292038,3,12,-12.0,-9.0,0.023 +2015-10-17 01:10:08:328489,3,13,-12.0,-8.5,0.019 +2015-10-17 01:10:08:364942,3,14,-12.0,-8.0,0.016 +2015-10-17 01:10:08:396430,3,15,-12.0,-7.5,0.013 +2015-10-17 01:10:08:430551,3,16,-12.0,-7.0,0.01 +2015-10-17 01:10:08:463821,3,17,-12.0,-6.5,0.009 +2015-10-17 01:10:08:497243,3,18,-12.0,-6.0,0.007 +2015-10-17 01:10:08:530797,3,19,-12.0,-5.5,0.006 +2015-10-17 01:10:08:564270,3,20,-12.0,-5.0,0.005 +2015-10-17 01:10:08:597596,3,21,-12.0,-4.5,0.006 +2015-10-17 01:10:08:631189,3,22,-12.0,-4.0,0.007 +2015-10-17 01:10:08:664949,3,23,-12.0,-3.5,0.009 +2015-10-17 01:10:08:697892,3,24,-12.0,-3.0,0.01 +2015-10-17 01:10:08:735174,3,25,-12.0,-2.5,0.013 +2015-10-17 01:10:08:735174,3,25,-12.0,-2.5,0.013 +2015-10-17 01:10:08:768955,3,26,-12.0,-2.0,0.016 +2015-10-17 01:10:08:804948,3,27,-12.0,-1.5,0.019 +2015-10-17 01:10:08:837747,3,28,-12.0,-1.0,0.023 +2015-10-17 01:10:08:873464,3,29,-12.0,-0.5,0.026 +2015-10-17 01:10:08:906893,3,30,-12.0,0.0,0.027 +2015-10-17 01:10:08:940047,3,31,-12.0,0.5,0.026 +2015-10-17 01:10:08:974879,3,32,-12.0,1.0,0.023 +2015-10-17 01:10:09:010792,3,33,-12.0,1.5,0.019 +2015-10-17 01:10:09:044847,3,34,-12.0,2.0,0.016 +2015-10-17 01:10:09:076020,3,35,-12.0,2.5,0.013 +2015-10-17 01:10:09:112016,3,36,-12.0,3.0,0.01 +2015-10-17 01:10:09:145528,3,37,-12.0,3.5,0.009 +2015-10-17 01:10:09:177649,3,38,-12.0,4.0,0.007 +2015-10-17 01:10:09:210585,3,39,-12.0,4.5,0.006 +2015-10-17 01:10:09:245212,3,40,-12.0,5.0,0.005 +2015-10-17 01:10:09:278986,3,41,-12.0,5.5,0.006 +2015-10-17 01:10:09:312762,3,42,-12.0,6.0,0.007 +2015-10-17 01:10:09:347908,3,43,-12.0,6.5,0.009 +2015-10-17 01:10:09:380944,3,44,-12.0,7.0,0.01 +2015-10-17 01:10:09:416991,3,45,-12.0,7.5,0.013 +2015-10-17 01:10:09:449733,3,46,-12.0,8.0,0.016 +2015-10-17 01:10:09:485115,3,47,-12.0,8.5,0.019 +2015-10-17 01:10:09:517919,3,48,-12.0,9.0,0.023 +2015-10-17 01:10:09:553376,3,49,-12.0,9.5,0.026 +2015-10-17 01:10:09:585561,3,50,-12.0,10.0,0.027 +2015-10-17 01:10:09:617410,3,51,-12.0,10.5,0.026 +2015-10-17 01:10:09:649686,3,52,-12.0,11.0,0.023 +2015-10-17 01:10:09:685373,3,53,-12.0,11.5,0.019 +2015-10-17 01:10:09:715920,3,54,-12.0,12.0,0.016 +2015-10-17 01:10:09:751148,3,55,-12.0,12.5,0.013 +2015-10-17 01:10:09:751148,3,55,-12.0,12.5,0.013 +2015-10-17 01:10:09:785537,3,56,-12.0,13.0,0.01 +2015-10-17 01:10:09:819802,3,57,-12.0,13.5,0.009 +2015-10-17 01:10:09:851732,3,58,-12.0,14.0,0.007 +2015-10-17 01:10:09:888237,3,59,-12.0,14.5,0.006 +2015-10-17 01:10:10:894932,4,0,-11.0,-15.0,0.006 +2015-10-17 01:10:10:894932,4,0,-11.0,-15.0,0.006 +2015-10-17 01:10:10:927253,4,1,-11.0,-14.5,0.007 +2015-10-17 01:10:10:959563,4,2,-11.0,-14.0,0.008 +2015-10-17 01:10:10:991209,4,3,-11.0,-13.5,0.01 +2015-10-17 01:10:11:023257,4,4,-11.0,-13.0,0.013 +2015-10-17 01:10:11:054457,4,5,-11.0,-12.5,0.017 +2015-10-17 01:10:11:087229,4,6,-11.0,-12.0,0.023 +2015-10-17 01:10:11:119231,4,7,-11.0,-11.5,0.031 +2015-10-17 01:10:11:151294,4,8,-11.0,-11.0,0.044 +2015-10-17 01:10:11:183334,4,9,-11.0,-10.5,0.057 +2015-10-17 01:10:11:217238,4,10,-11.0,-10.0,0.064 +2015-10-17 01:10:11:254149,4,11,-11.0,-9.5,0.057 +2015-10-17 01:10:11:286818,4,12,-11.0,-9.0,0.044 +2015-10-17 01:10:11:322539,4,13,-11.0,-8.5,0.031 +2015-10-17 01:10:11:358527,4,14,-11.0,-8.0,0.023 +2015-10-17 01:10:11:390881,4,15,-11.0,-7.5,0.017 +2015-10-17 01:10:11:422643,4,16,-11.0,-7.0,0.013 +2015-10-17 01:10:11:454998,4,17,-11.0,-6.5,0.01 +2015-10-17 01:10:11:486613,4,18,-11.0,-6.0,0.008 +2015-10-17 01:10:11:521955,4,19,-11.0,-5.5,0.007 +2015-10-17 01:10:11:556174,4,20,-11.0,-5.0,0.006 +2015-10-17 01:10:11:587364,4,21,-11.0,-4.5,0.007 +2015-10-17 01:10:11:623133,4,22,-11.0,-4.0,0.008 +2015-10-17 01:10:11:655174,4,23,-11.0,-3.5,0.01 +2015-10-17 01:10:11:689038,4,24,-11.0,-3.0,0.013 +2015-10-17 01:10:11:720747,4,25,-11.0,-2.5,0.017 +2015-10-17 01:10:11:757098,4,26,-11.0,-2.0,0.023 +2015-10-17 01:10:11:791013,4,27,-11.0,-1.5,0.031 +2015-10-17 01:10:11:822618,4,28,-11.0,-1.0,0.044 +2015-10-17 01:10:11:855171,4,29,-11.0,-0.5,0.057 +2015-10-17 01:10:11:891632,4,30,-11.0,0.0,0.064 +2015-10-17 01:10:11:927706,4,31,-11.0,0.5,0.057 +2015-10-17 01:10:11:927706,4,31,-11.0,0.5,0.057 +2015-10-17 01:10:11:959218,4,32,-11.0,1.0,0.044 +2015-10-17 01:10:11:995385,4,33,-11.0,1.5,0.031 +2015-10-17 01:10:12:031864,4,34,-11.0,2.0,0.023 +2015-10-17 01:10:12:066632,4,35,-11.0,2.5,0.017 +2015-10-17 01:10:12:099431,4,36,-11.0,3.0,0.013 +2015-10-17 01:10:12:130601,4,37,-11.0,3.5,0.01 +2015-10-17 01:10:12:165627,4,38,-11.0,4.0,0.008 +2015-10-17 01:10:12:200031,4,39,-11.0,4.5,0.007 +2015-10-17 01:10:12:236682,4,40,-11.0,5.0,0.006 +2015-10-17 01:10:12:273207,4,41,-11.0,5.5,0.007 +2015-10-17 01:10:12:309653,4,42,-11.0,6.0,0.008 +2015-10-17 01:10:12:344161,4,43,-11.0,6.5,0.01 +2015-10-17 01:10:12:380017,4,44,-11.0,7.0,0.013 +2015-10-17 01:10:12:416901,4,45,-11.0,7.5,0.017 +2015-10-17 01:10:12:451914,4,46,-11.0,8.0,0.023 +2015-10-17 01:10:12:485224,4,47,-11.0,8.5,0.031 +2015-10-17 01:10:12:516762,4,48,-11.0,9.0,0.044 +2015-10-17 01:10:12:551532,4,49,-11.0,9.5,0.057 +2015-10-17 01:10:12:585031,4,50,-11.0,10.0,0.064 +2015-10-17 01:10:12:621680,4,51,-11.0,10.5,0.057 +2015-10-17 01:10:12:658364,4,52,-11.0,11.0,0.044 +2015-10-17 01:10:12:691497,4,53,-11.0,11.5,0.031 +2015-10-17 01:10:12:725951,4,54,-11.0,12.0,0.023 +2015-10-17 01:10:12:762422,4,55,-11.0,12.5,0.017 +2015-10-17 01:10:12:798397,4,56,-11.0,13.0,0.013 +2015-10-17 01:10:12:830715,4,57,-11.0,13.5,0.01 +2015-10-17 01:10:12:864433,4,58,-11.0,14.0,0.008 +2015-10-17 01:10:12:895581,4,59,-11.0,14.5,0.007 +2015-10-17 01:10:12:895581,4,59,-11.0,14.5,0.007 +2015-10-17 01:10:13:900660,5,0,-10.0,-15.0,0.006 +2015-10-17 01:10:13:932583,5,1,-10.0,-14.5,0.007 +2015-10-17 01:10:13:964514,5,2,-10.0,-14.0,0.009 +2015-10-17 01:10:13:996418,5,3,-10.0,-13.5,0.011 +2015-10-17 01:10:14:027750,5,4,-10.0,-13.0,0.014 +2015-10-17 01:10:14:061392,5,5,-10.0,-12.5,0.019 +2015-10-17 01:10:14:094280,5,6,-10.0,-12.0,0.027 +2015-10-17 01:10:14:125948,5,7,-10.0,-11.5,0.04 +2015-10-17 01:10:14:125948,5,7,-10.0,-11.5,0.04 +2015-10-17 01:10:14:157860,5,8,-10.0,-11.0,0.064 +2015-10-17 01:10:14:193802,5,9,-10.0,-10.5,0.099 +2015-10-17 01:10:14:223803,5,10,-10.0,-10.0,0.117 +2015-10-17 01:10:14:258560,5,11,-10.0,-9.5,0.099 +2015-10-17 01:10:14:292113,5,12,-10.0,-9.0,0.064 +2015-10-17 01:10:14:325896,5,13,-10.0,-8.5,0.04 +2015-10-17 01:10:14:359015,5,14,-10.0,-8.0,0.027 +2015-10-17 01:10:14:394361,5,15,-10.0,-7.5,0.019 +2015-10-17 01:10:14:427378,5,16,-10.0,-7.0,0.014 +2015-10-17 01:10:14:460068,5,17,-10.0,-6.5,0.011 +2015-10-17 01:10:14:492424,5,18,-10.0,-6.0,0.009 +2015-10-17 01:10:14:526884,5,19,-10.0,-5.5,0.007 +2015-10-17 01:10:14:558964,5,20,-10.0,-5.0,0.006 +2015-10-17 01:10:14:594491,5,21,-10.0,-4.5,0.007 +2015-10-17 01:10:14:627530,5,22,-10.0,-4.0,0.009 +2015-10-17 01:10:14:662908,5,23,-10.0,-3.5,0.011 +2015-10-17 01:10:14:697918,5,24,-10.0,-3.0,0.014 +2015-10-17 01:10:14:734430,5,25,-10.0,-2.5,0.019 +2015-10-17 01:10:14:766251,5,26,-10.0,-2.0,0.027 +2015-10-17 01:10:14:798662,5,27,-10.0,-1.5,0.04 +2015-10-17 01:10:14:831132,5,28,-10.0,-1.0,0.064 +2015-10-17 01:10:14:862344,5,29,-10.0,-0.5,0.099 +2015-10-17 01:10:14:897145,5,30,-10.0,0.0,0.117 +2015-10-17 01:10:14:934504,5,31,-10.0,0.5,0.099 +2015-10-17 01:10:14:966439,5,32,-10.0,1.0,0.064 +2015-10-17 01:10:14:998867,5,33,-10.0,1.5,0.04 +2015-10-17 01:10:15:030519,5,34,-10.0,2.0,0.027 +2015-10-17 01:10:15:065654,5,35,-10.0,2.5,0.019 +2015-10-17 01:10:15:102041,5,36,-10.0,3.0,0.014 +2015-10-17 01:10:15:134852,5,37,-10.0,3.5,0.011 +2015-10-17 01:10:15:134852,5,37,-10.0,3.5,0.011 +2015-10-17 01:10:15:166686,5,38,-10.0,4.0,0.009 +2015-10-17 01:10:15:199019,5,39,-10.0,4.5,0.007 +2015-10-17 01:10:15:232250,5,40,-10.0,5.0,0.006 +2015-10-17 01:10:15:268177,5,41,-10.0,5.5,0.007 +2015-10-17 01:10:15:305460,5,42,-10.0,6.0,0.009 +2015-10-17 01:10:15:337927,5,43,-10.0,6.5,0.011 +2015-10-17 01:10:15:369324,5,44,-10.0,7.0,0.014 +2015-10-17 01:10:15:401319,5,45,-10.0,7.5,0.019 +2015-10-17 01:10:15:439162,5,46,-10.0,8.0,0.027 +2015-10-17 01:10:15:475548,5,47,-10.0,8.5,0.04 +2015-10-17 01:10:15:511517,5,48,-10.0,9.0,0.064 +2015-10-17 01:10:15:547465,5,49,-10.0,9.5,0.099 +2015-10-17 01:10:15:582139,5,50,-10.0,10.0,0.117 +2015-10-17 01:10:15:616816,5,51,-10.0,10.5,0.099 +2015-10-17 01:10:15:650427,5,52,-10.0,11.0,0.064 +2015-10-17 01:10:15:682234,5,53,-10.0,11.5,0.04 +2015-10-17 01:10:15:715656,5,54,-10.0,12.0,0.027 +2015-10-17 01:10:15:748246,5,55,-10.0,12.5,0.019 +2015-10-17 01:10:15:781193,5,56,-10.0,13.0,0.014 +2015-10-17 01:10:15:814155,5,57,-10.0,13.5,0.011 +2015-10-17 01:10:15:846714,5,58,-10.0,14.0,0.009 +2015-10-17 01:10:15:878118,5,59,-10.0,14.5,0.007 +2015-10-17 01:10:15:878118,5,59,-10.0,14.5,0.007 +2015-10-17 01:10:16:880203,6,0,-9.0,-15.0,0.006 +2015-10-17 01:10:16:912444,6,1,-9.0,-14.5,0.007 +2015-10-17 01:10:16:944154,6,2,-9.0,-14.0,0.008 +2015-10-17 01:10:16:976145,6,3,-9.0,-13.5,0.01 +2015-10-17 01:10:17:008069,6,4,-9.0,-13.0,0.013 +2015-10-17 01:10:17:043987,6,5,-9.0,-12.5,0.017 +2015-10-17 01:10:17:076536,6,6,-9.0,-12.0,0.023 +2015-10-17 01:10:17:110760,6,7,-9.0,-11.5,0.031 +2015-10-17 01:10:17:144588,6,8,-9.0,-11.0,0.044 +2015-10-17 01:10:17:177618,6,9,-9.0,-10.5,0.057 +2015-10-17 01:10:17:210139,6,10,-9.0,-10.0,0.064 +2015-10-17 01:10:17:241657,6,11,-9.0,-9.5,0.057 +2015-10-17 01:10:17:274240,6,12,-9.0,-9.0,0.044 +2015-10-17 01:10:17:310946,6,13,-9.0,-8.5,0.031 +2015-10-17 01:10:17:310946,6,13,-9.0,-8.5,0.031 +2015-10-17 01:10:17:344424,6,14,-9.0,-8.0,0.023 +2015-10-17 01:10:17:377892,6,15,-9.0,-7.5,0.017 +2015-10-17 01:10:17:413463,6,16,-9.0,-7.0,0.013 +2015-10-17 01:10:17:445070,6,17,-9.0,-6.5,0.01 +2015-10-17 01:10:17:478452,6,18,-9.0,-6.0,0.008 +2015-10-17 01:10:17:511746,6,19,-9.0,-5.5,0.007 +2015-10-17 01:10:17:548187,6,20,-9.0,-5.0,0.006 +2015-10-17 01:10:17:580722,6,21,-9.0,-4.5,0.007 +2015-10-17 01:10:17:612879,6,22,-9.0,-4.0,0.008 +2015-10-17 01:10:17:645329,6,23,-9.0,-3.5,0.01 +2015-10-17 01:10:17:680922,6,24,-9.0,-3.0,0.013 +2015-10-17 01:10:17:713952,6,25,-9.0,-2.5,0.017 +2015-10-17 01:10:17:745579,6,26,-9.0,-2.0,0.023 +2015-10-17 01:10:17:783026,6,27,-9.0,-1.5,0.031 +2015-10-17 01:10:17:816128,6,28,-9.0,-1.0,0.044 +2015-10-17 01:10:17:852834,6,29,-9.0,-0.5,0.057 +2015-10-17 01:10:17:886029,6,30,-9.0,0.0,0.064 +2015-10-17 01:10:17:921751,6,31,-9.0,0.5,0.057 +2015-10-17 01:10:17:958218,6,32,-9.0,1.0,0.044 +2015-10-17 01:10:17:991664,6,33,-9.0,1.5,0.031 +2015-10-17 01:10:18:025650,6,34,-9.0,2.0,0.023 +2015-10-17 01:10:18:057449,6,35,-9.0,2.5,0.017 +2015-10-17 01:10:18:093529,6,36,-9.0,3.0,0.013 +2015-10-17 01:10:18:130130,6,37,-9.0,3.5,0.01 +2015-10-17 01:10:18:163588,6,38,-9.0,4.0,0.008 +2015-10-17 01:10:18:197552,6,39,-9.0,4.5,0.007 +2015-10-17 01:10:18:230798,6,40,-9.0,5.0,0.006 +2015-10-17 01:10:18:264394,6,41,-9.0,5.5,0.007 +2015-10-17 01:10:18:300409,6,42,-9.0,6.0,0.008 +2015-10-17 01:10:18:332864,6,43,-9.0,6.5,0.01 +2015-10-17 01:10:18:332864,6,43,-9.0,6.5,0.01 +2015-10-17 01:10:18:366187,6,44,-9.0,7.0,0.013 +2015-10-17 01:10:18:402087,6,45,-9.0,7.5,0.017 +2015-10-17 01:10:18:434840,6,46,-9.0,8.0,0.023 +2015-10-17 01:10:18:467498,6,47,-9.0,8.5,0.031 +2015-10-17 01:10:18:499393,6,48,-9.0,9.0,0.044 +2015-10-17 01:10:18:532984,6,49,-9.0,9.5,0.057 +2015-10-17 01:10:18:563463,6,50,-9.0,10.0,0.064 +2015-10-17 01:10:18:597527,6,51,-9.0,10.5,0.057 +2015-10-17 01:10:18:631020,6,52,-9.0,11.0,0.044 +2015-10-17 01:10:18:664740,6,53,-9.0,11.5,0.031 +2015-10-17 01:10:18:699570,6,54,-9.0,12.0,0.023 +2015-10-17 01:10:18:730717,6,55,-9.0,12.5,0.017 +2015-10-17 01:10:18:765755,6,56,-9.0,13.0,0.013 +2015-10-17 01:10:18:799645,6,57,-9.0,13.5,0.01 +2015-10-17 01:10:18:831792,6,58,-9.0,14.0,0.008 +2015-10-17 01:10:18:868513,6,59,-9.0,14.5,0.007 +2015-10-17 01:10:18:868513,6,59,-9.0,14.5,0.007 +2015-10-17 01:10:19:872857,7,0,-8.0,-15.0,0.005 +2015-10-17 01:10:19:906091,7,1,-8.0,-14.5,0.006 +2015-10-17 01:10:19:936892,7,2,-8.0,-14.0,0.007 +2015-10-17 01:10:19:969254,7,3,-8.0,-13.5,0.009 +2015-10-17 01:10:20:000871,7,4,-8.0,-13.0,0.01 +2015-10-17 01:10:20:031867,7,5,-8.0,-12.5,0.013 +2015-10-17 01:10:20:068737,7,6,-8.0,-12.0,0.016 +2015-10-17 01:10:20:102176,7,7,-8.0,-11.5,0.019 +2015-10-17 01:10:20:135453,7,8,-8.0,-11.0,0.023 +2015-10-17 01:10:20:168366,7,9,-8.0,-10.5,0.026 +2015-10-17 01:10:20:199990,7,10,-8.0,-10.0,0.027 +2015-10-17 01:10:20:236802,7,11,-8.0,-9.5,0.026 +2015-10-17 01:10:20:269355,7,12,-8.0,-9.0,0.023 +2015-10-17 01:10:20:303868,7,13,-8.0,-8.5,0.019 +2015-10-17 01:10:20:338438,7,14,-8.0,-8.0,0.016 +2015-10-17 01:10:20:374526,7,15,-8.0,-7.5,0.013 +2015-10-17 01:10:20:407836,7,16,-8.0,-7.0,0.01 +2015-10-17 01:10:20:441102,7,17,-8.0,-6.5,0.009 +2015-10-17 01:10:20:477282,7,18,-8.0,-6.0,0.007 +2015-10-17 01:10:20:477282,7,18,-8.0,-6.0,0.007 +2015-10-17 01:10:20:512721,7,19,-8.0,-5.5,0.006 +2015-10-17 01:10:20:544349,7,20,-8.0,-5.0,0.005 +2015-10-17 01:10:20:578533,7,21,-8.0,-4.5,0.006 +2015-10-17 01:10:20:614774,7,22,-8.0,-4.0,0.007 +2015-10-17 01:10:20:649538,7,23,-8.0,-3.5,0.009 +2015-10-17 01:10:20:681766,7,24,-8.0,-3.0,0.01 +2015-10-17 01:10:20:719281,7,25,-8.0,-2.5,0.013 +2015-10-17 01:10:20:751070,7,26,-8.0,-2.0,0.016 +2015-10-17 01:10:20:786972,7,27,-8.0,-1.5,0.019 +2015-10-17 01:10:20:823333,7,28,-8.0,-1.0,0.023 +2015-10-17 01:10:20:855623,7,29,-8.0,-0.5,0.026 +2015-10-17 01:10:20:888237,7,30,-8.0,0.0,0.027 +2015-10-17 01:10:20:924550,7,31,-8.0,0.5,0.026 +2015-10-17 01:10:20:960196,7,32,-8.0,1.0,0.023 +2015-10-17 01:10:20:992639,7,33,-8.0,1.5,0.019 +2015-10-17 01:10:21:024257,7,34,-8.0,2.0,0.016 +2015-10-17 01:10:21:055946,7,35,-8.0,2.5,0.013 +2015-10-17 01:10:21:088198,7,36,-8.0,3.0,0.01 +2015-10-17 01:10:21:124665,7,37,-8.0,3.5,0.009 +2015-10-17 01:10:21:161128,7,38,-8.0,4.0,0.007 +2015-10-17 01:10:21:197586,7,39,-8.0,4.5,0.006 +2015-10-17 01:10:21:234060,7,40,-8.0,5.0,0.005 +2015-10-17 01:10:21:267208,7,41,-8.0,5.5,0.006 +2015-10-17 01:10:21:303272,7,42,-8.0,6.0,0.007 +2015-10-17 01:10:21:336123,7,43,-8.0,6.5,0.009 +2015-10-17 01:10:21:369910,7,44,-8.0,7.0,0.01 +2015-10-17 01:10:21:402072,7,45,-8.0,7.5,0.013 +2015-10-17 01:10:21:436794,7,46,-8.0,8.0,0.016 +2015-10-17 01:10:21:474766,7,47,-8.0,8.5,0.019 +2015-10-17 01:10:21:506772,7,48,-8.0,9.0,0.023 +2015-10-17 01:10:21:506772,7,48,-8.0,9.0,0.023 +2015-10-17 01:10:21:541635,7,49,-8.0,9.5,0.026 +2015-10-17 01:10:21:575390,7,50,-8.0,10.0,0.027 +2015-10-17 01:10:21:607546,7,51,-8.0,10.5,0.026 +2015-10-17 01:10:21:640945,7,52,-8.0,11.0,0.023 +2015-10-17 01:10:21:673132,7,53,-8.0,11.5,0.019 +2015-10-17 01:10:21:705161,7,54,-8.0,12.0,0.016 +2015-10-17 01:10:21:737914,7,55,-8.0,12.5,0.013 +2015-10-17 01:10:21:769213,7,56,-8.0,13.0,0.01 +2015-10-17 01:10:21:801208,7,57,-8.0,13.5,0.009 +2015-10-17 01:10:21:836760,7,58,-8.0,14.0,0.007 +2015-10-17 01:10:21:869261,7,59,-8.0,14.5,0.006 +2015-10-17 01:10:21:869261,7,59,-8.0,14.5,0.006 +2015-10-17 01:10:22:872097,8,0,-7.0,-15.0,0.005 +2015-10-17 01:10:22:908046,8,1,-7.0,-14.5,0.005 +2015-10-17 01:10:22:939276,8,2,-7.0,-14.0,0.006 +2015-10-17 01:10:22:971985,8,3,-7.0,-13.5,0.007 +2015-10-17 01:10:23:004347,8,4,-7.0,-13.0,0.008 +2015-10-17 01:10:23:037794,8,5,-7.0,-12.5,0.009 +2015-10-17 01:10:23:073112,8,6,-7.0,-12.0,0.01 +2015-10-17 01:10:23:104830,8,7,-7.0,-11.5,0.012 +2015-10-17 01:10:23:137391,8,8,-7.0,-11.0,0.013 +2015-10-17 01:10:23:173602,8,9,-7.0,-10.5,0.014 +2015-10-17 01:10:23:206537,8,10,-7.0,-10.0,0.014 +2015-10-17 01:10:23:238109,8,11,-7.0,-9.5,0.014 +2015-10-17 01:10:23:270001,8,12,-7.0,-9.0,0.013 +2015-10-17 01:10:23:303388,8,13,-7.0,-8.5,0.012 +2015-10-17 01:10:23:338737,8,14,-7.0,-8.0,0.01 +2015-10-17 01:10:23:370029,8,15,-7.0,-7.5,0.009 +2015-10-17 01:10:23:401871,8,16,-7.0,-7.0,0.008 +2015-10-17 01:10:23:437148,8,17,-7.0,-6.5,0.007 +2015-10-17 01:10:23:469233,8,18,-7.0,-6.0,0.006 +2015-10-17 01:10:23:503366,8,19,-7.0,-5.5,0.005 +2015-10-17 01:10:23:538369,8,20,-7.0,-5.0,0.005 +2015-10-17 01:10:23:570693,8,21,-7.0,-4.5,0.005 +2015-10-17 01:10:23:606528,8,22,-7.0,-4.0,0.006 +2015-10-17 01:10:23:638447,8,23,-7.0,-3.5,0.007 +2015-10-17 01:10:23:671165,8,24,-7.0,-3.0,0.008 +2015-10-17 01:10:23:703074,8,25,-7.0,-2.5,0.009 +2015-10-17 01:10:23:703074,8,25,-7.0,-2.5,0.009 +2015-10-17 01:10:23:735399,8,26,-7.0,-2.0,0.01 +2015-10-17 01:10:23:768276,8,27,-7.0,-1.5,0.012 +2015-10-17 01:10:23:799932,8,28,-7.0,-1.0,0.013 +2015-10-17 01:10:23:831985,8,29,-7.0,-0.5,0.014 +2015-10-17 01:10:23:863513,8,30,-7.0,0.0,0.014 +2015-10-17 01:10:23:895468,8,31,-7.0,0.5,0.014 +2015-10-17 01:10:23:929825,8,32,-7.0,1.0,0.013 +2015-10-17 01:10:23:963522,8,33,-7.0,1.5,0.012 +2015-10-17 01:10:23:998923,8,34,-7.0,2.0,0.01 +2015-10-17 01:10:24:030514,8,35,-7.0,2.5,0.009 +2015-10-17 01:10:24:062096,8,36,-7.0,3.0,0.008 +2015-10-17 01:10:24:094876,8,37,-7.0,3.5,0.007 +2015-10-17 01:10:24:129719,8,38,-7.0,4.0,0.006 +2015-10-17 01:10:24:165517,8,39,-7.0,4.5,0.005 +2015-10-17 01:10:24:198906,8,40,-7.0,5.0,0.005 +2015-10-17 01:10:24:230832,8,41,-7.0,5.5,0.005 +2015-10-17 01:10:24:262810,8,42,-7.0,6.0,0.006 +2015-10-17 01:10:24:294333,8,43,-7.0,6.5,0.007 +2015-10-17 01:10:24:327033,8,44,-7.0,7.0,0.008 +2015-10-17 01:10:24:360629,8,45,-7.0,7.5,0.009 +2015-10-17 01:10:24:397357,8,46,-7.0,8.0,0.01 +2015-10-17 01:10:24:434107,8,47,-7.0,8.5,0.012 +2015-10-17 01:10:24:470741,8,48,-7.0,9.0,0.013 +2015-10-17 01:10:24:505201,8,49,-7.0,9.5,0.014 +2015-10-17 01:10:24:542294,8,50,-7.0,10.0,0.014 +2015-10-17 01:10:24:575853,8,51,-7.0,10.5,0.014 +2015-10-17 01:10:24:607851,8,52,-7.0,11.0,0.013 +2015-10-17 01:10:24:641096,8,53,-7.0,11.5,0.012 +2015-10-17 01:10:24:674963,8,54,-7.0,12.0,0.01 +2015-10-17 01:10:24:711276,8,55,-7.0,12.5,0.009 +2015-10-17 01:10:24:711276,8,55,-7.0,12.5,0.009 +2015-10-17 01:10:24:742088,8,56,-7.0,13.0,0.008 +2015-10-17 01:10:24:777479,8,57,-7.0,13.5,0.007 +2015-10-17 01:10:24:814099,8,58,-7.0,14.0,0.006 +2015-10-17 01:10:24:845952,8,59,-7.0,14.5,0.005 +2015-10-17 01:10:25:851142,9,0,-6.0,-15.0,0.004 +2015-10-17 01:10:25:851142,9,0,-6.0,-15.0,0.004 +2015-10-17 01:10:25:882512,9,1,-6.0,-14.5,0.004 +2015-10-17 01:10:25:914205,9,2,-6.0,-14.0,0.005 +2015-10-17 01:10:25:949278,9,3,-6.0,-13.5,0.005 +2015-10-17 01:10:25:980765,9,4,-6.0,-13.0,0.006 +2015-10-17 01:10:26:012696,9,5,-6.0,-12.5,0.007 +2015-10-17 01:10:26:044562,9,6,-6.0,-12.0,0.007 +2015-10-17 01:10:26:076634,9,7,-6.0,-11.5,0.008 +2015-10-17 01:10:26:107759,9,8,-6.0,-11.0,0.008 +2015-10-17 01:10:26:144210,9,9,-6.0,-10.5,0.009 +2015-10-17 01:10:26:179090,9,10,-6.0,-10.0,0.009 +2015-10-17 01:10:26:211642,9,11,-6.0,-9.5,0.009 +2015-10-17 01:10:26:243853,9,12,-6.0,-9.0,0.008 +2015-10-17 01:10:26:278218,9,13,-6.0,-8.5,0.008 +2015-10-17 01:10:26:310609,9,14,-6.0,-8.0,0.007 +2015-10-17 01:10:26:343462,9,15,-6.0,-7.5,0.007 +2015-10-17 01:10:26:376996,9,16,-6.0,-7.0,0.006 +2015-10-17 01:10:26:408776,9,17,-6.0,-6.5,0.005 +2015-10-17 01:10:26:443707,9,18,-6.0,-6.0,0.005 +2015-10-17 01:10:26:478022,9,19,-6.0,-5.5,0.004 +2015-10-17 01:10:26:510201,9,20,-6.0,-5.0,0.004 +2015-10-17 01:10:26:542002,9,21,-6.0,-4.5,0.004 +2015-10-17 01:10:26:575927,9,22,-6.0,-4.0,0.005 +2015-10-17 01:10:26:608901,9,23,-6.0,-3.5,0.005 +2015-10-17 01:10:26:643759,9,24,-6.0,-3.0,0.006 +2015-10-17 01:10:26:676812,9,25,-6.0,-2.5,0.007 +2015-10-17 01:10:26:710752,9,26,-6.0,-2.0,0.007 +2015-10-17 01:10:26:745157,9,27,-6.0,-1.5,0.008 +2015-10-17 01:10:26:782285,9,28,-6.0,-1.0,0.008 +2015-10-17 01:10:26:814737,9,29,-6.0,-0.5,0.009 +2015-10-17 01:10:26:846137,9,30,-6.0,0.0,0.009 +2015-10-17 01:10:26:878129,9,31,-6.0,0.5,0.009 +2015-10-17 01:10:26:878129,9,31,-6.0,0.5,0.009 +2015-10-17 01:10:26:910871,9,32,-6.0,1.0,0.008 +2015-10-17 01:10:26:945379,9,33,-6.0,1.5,0.008 +2015-10-17 01:10:26:977601,9,34,-6.0,2.0,0.007 +2015-10-17 01:10:27:009346,9,35,-6.0,2.5,0.007 +2015-10-17 01:10:27:042915,9,36,-6.0,3.0,0.006 +2015-10-17 01:10:27:075840,9,37,-6.0,3.5,0.005 +2015-10-17 01:10:27:108272,9,38,-6.0,4.0,0.005 +2015-10-17 01:10:27:145220,9,39,-6.0,4.5,0.004 +2015-10-17 01:10:27:176785,9,40,-6.0,5.0,0.004 +2015-10-17 01:10:27:209403,9,41,-6.0,5.5,0.004 +2015-10-17 01:10:27:241621,9,42,-6.0,6.0,0.005 +2015-10-17 01:10:27:273591,9,43,-6.0,6.5,0.005 +2015-10-17 01:10:27:305608,9,44,-6.0,7.0,0.006 +2015-10-17 01:10:27:338537,9,45,-6.0,7.5,0.007 +2015-10-17 01:10:27:375998,9,46,-6.0,8.0,0.007 +2015-10-17 01:10:27:409389,9,47,-6.0,8.5,0.008 +2015-10-17 01:10:27:441981,9,48,-6.0,9.0,0.008 +2015-10-17 01:10:27:474661,9,49,-6.0,9.5,0.009 +2015-10-17 01:10:27:506788,9,50,-6.0,10.0,0.009 +2015-10-17 01:10:27:538186,9,51,-6.0,10.5,0.009 +2015-10-17 01:10:27:573210,9,52,-6.0,11.0,0.008 +2015-10-17 01:10:27:609636,9,53,-6.0,11.5,0.008 +2015-10-17 01:10:27:645433,9,54,-6.0,12.0,0.007 +2015-10-17 01:10:27:680039,9,55,-6.0,12.5,0.007 +2015-10-17 01:10:27:717447,9,56,-6.0,13.0,0.006 +2015-10-17 01:10:27:753420,9,57,-6.0,13.5,0.005 +2015-10-17 01:10:27:786047,9,58,-6.0,14.0,0.005 +2015-10-17 01:10:27:822684,9,59,-6.0,14.5,0.004 +2015-10-17 01:10:27:822684,9,59,-6.0,14.5,0.004 +2015-10-17 01:10:28:828712,10,0,-5.0,-15.0,0.003 +2015-10-17 01:10:28:861485,10,1,-5.0,-14.5,0.004 +2015-10-17 01:10:28:893762,10,2,-5.0,-14.0,0.004 +2015-10-17 01:10:28:926925,10,3,-5.0,-13.5,0.004 +2015-10-17 01:10:28:959605,10,4,-5.0,-13.0,0.005 +2015-10-17 01:10:28:994194,10,5,-5.0,-12.5,0.005 +2015-10-17 01:10:29:025465,10,6,-5.0,-12.0,0.005 +2015-10-17 01:10:29:061553,10,7,-5.0,-11.5,0.006 +2015-10-17 01:10:29:061553,10,7,-5.0,-11.5,0.006 +2015-10-17 01:10:29:092915,10,8,-5.0,-11.0,0.006 +2015-10-17 01:10:29:127665,10,9,-5.0,-10.5,0.006 +2015-10-17 01:10:29:160008,10,10,-5.0,-10.0,0.006 +2015-10-17 01:10:29:193397,10,11,-5.0,-9.5,0.006 +2015-10-17 01:10:29:227262,10,12,-5.0,-9.0,0.006 +2015-10-17 01:10:29:260812,10,13,-5.0,-8.5,0.006 +2015-10-17 01:10:29:293301,10,14,-5.0,-8.0,0.005 +2015-10-17 01:10:29:329411,10,15,-5.0,-7.5,0.005 +2015-10-17 01:10:29:360904,10,16,-5.0,-7.0,0.005 +2015-10-17 01:10:29:393512,10,17,-5.0,-6.5,0.004 +2015-10-17 01:10:29:427829,10,18,-5.0,-6.0,0.004 +2015-10-17 01:10:29:461615,10,19,-5.0,-5.5,0.004 +2015-10-17 01:10:29:493935,10,20,-5.0,-5.0,0.003 +2015-10-17 01:10:29:528760,10,21,-5.0,-4.5,0.004 +2015-10-17 01:10:29:560770,10,22,-5.0,-4.0,0.004 +2015-10-17 01:10:29:593842,10,23,-5.0,-3.5,0.004 +2015-10-17 01:10:29:625428,10,24,-5.0,-3.0,0.005 +2015-10-17 01:10:29:660969,10,25,-5.0,-2.5,0.005 +2015-10-17 01:10:29:696475,10,26,-5.0,-2.0,0.005 +2015-10-17 01:10:29:729870,10,27,-5.0,-1.5,0.006 +2015-10-17 01:10:29:762622,10,28,-5.0,-1.0,0.006 +2015-10-17 01:10:29:795361,10,29,-5.0,-0.5,0.006 +2015-10-17 01:10:29:828936,10,30,-5.0,0.0,0.006 +2015-10-17 01:10:29:860340,10,31,-5.0,0.5,0.006 +2015-10-17 01:10:29:891933,10,32,-5.0,1.0,0.006 +2015-10-17 01:10:29:924437,10,33,-5.0,1.5,0.006 +2015-10-17 01:10:29:956080,10,34,-5.0,2.0,0.005 +2015-10-17 01:10:29:988055,10,35,-5.0,2.5,0.005 +2015-10-17 01:10:30:022861,10,36,-5.0,3.0,0.005 +2015-10-17 01:10:30:055080,10,37,-5.0,3.5,0.004 +2015-10-17 01:10:30:085505,10,38,-5.0,4.0,0.004 +2015-10-17 01:10:30:085505,10,38,-5.0,4.0,0.004 +2015-10-17 01:10:30:116686,10,39,-5.0,4.5,0.004 +2015-10-17 01:10:30:148726,10,40,-5.0,5.0,0.003 +2015-10-17 01:10:30:181020,10,41,-5.0,5.5,0.004 +2015-10-17 01:10:30:213907,10,42,-5.0,6.0,0.004 +2015-10-17 01:10:30:248527,10,43,-5.0,6.5,0.004 +2015-10-17 01:10:30:279342,10,44,-5.0,7.0,0.005 +2015-10-17 01:10:30:311786,10,45,-5.0,7.5,0.005 +2015-10-17 01:10:30:346510,10,46,-5.0,8.0,0.005 +2015-10-17 01:10:30:381671,10,47,-5.0,8.5,0.006 +2015-10-17 01:10:30:412831,10,48,-5.0,9.0,0.006 +2015-10-17 01:10:30:444324,10,49,-5.0,9.5,0.006 +2015-10-17 01:10:30:476394,10,50,-5.0,10.0,0.006 +2015-10-17 01:10:30:509052,10,51,-5.0,10.5,0.006 +2015-10-17 01:10:30:544368,10,52,-5.0,11.0,0.006 +2015-10-17 01:10:30:580575,10,53,-5.0,11.5,0.006 +2015-10-17 01:10:30:613634,10,54,-5.0,12.0,0.005 +2015-10-17 01:10:30:644759,10,55,-5.0,12.5,0.005 +2015-10-17 01:10:30:676836,10,56,-5.0,13.0,0.005 +2015-10-17 01:10:30:709192,10,57,-5.0,13.5,0.004 +2015-10-17 01:10:30:740547,10,58,-5.0,14.0,0.004 +2015-10-17 01:10:30:774633,10,59,-5.0,14.5,0.004 +2015-10-17 01:10:30:774633,10,59,-5.0,14.5,0.004 +2015-10-17 01:10:31:777410,11,0,-4.0,-15.0,0.004 +2015-10-17 01:10:31:810412,11,1,-4.0,-14.5,0.004 +2015-10-17 01:10:31:844168,11,2,-4.0,-14.0,0.005 +2015-10-17 01:10:31:878057,11,3,-4.0,-13.5,0.005 +2015-10-17 01:10:31:910835,11,4,-4.0,-13.0,0.006 +2015-10-17 01:10:31:944819,11,5,-4.0,-12.5,0.007 +2015-10-17 01:10:31:978018,11,6,-4.0,-12.0,0.007 +2015-10-17 01:10:32:014394,11,7,-4.0,-11.5,0.008 +2015-10-17 01:10:32:050719,11,8,-4.0,-11.0,0.008 +2015-10-17 01:10:32:086971,11,9,-4.0,-10.5,0.009 +2015-10-17 01:10:32:121303,11,10,-4.0,-10.0,0.009 +2015-10-17 01:10:32:157730,11,11,-4.0,-9.5,0.009 +2015-10-17 01:10:32:194350,11,12,-4.0,-9.0,0.008 +2015-10-17 01:10:32:194350,11,12,-4.0,-9.0,0.008 +2015-10-17 01:10:32:229418,11,13,-4.0,-8.5,0.008 +2015-10-17 01:10:32:261171,11,14,-4.0,-8.0,0.007 +2015-10-17 01:10:32:295994,11,15,-4.0,-7.5,0.007 +2015-10-17 01:10:32:330075,11,16,-4.0,-7.0,0.006 +2015-10-17 01:10:32:367105,11,17,-4.0,-6.5,0.005 +2015-10-17 01:10:32:403028,11,18,-4.0,-6.0,0.005 +2015-10-17 01:10:32:439775,11,19,-4.0,-5.5,0.004 +2015-10-17 01:10:32:475759,11,20,-4.0,-5.0,0.004 +2015-10-17 01:10:32:512414,11,21,-4.0,-4.5,0.004 +2015-10-17 01:10:32:546736,11,22,-4.0,-4.0,0.005 +2015-10-17 01:10:32:582588,11,23,-4.0,-3.5,0.005 +2015-10-17 01:10:32:619848,11,24,-4.0,-3.0,0.006 +2015-10-17 01:10:32:651909,11,25,-4.0,-2.5,0.007 +2015-10-17 01:10:32:688364,11,26,-4.0,-2.0,0.007 +2015-10-17 01:10:32:724821,11,27,-4.0,-1.5,0.008 +2015-10-17 01:10:32:760822,11,28,-4.0,-1.0,0.008 +2015-10-17 01:10:32:794209,11,29,-4.0,-0.5,0.009 +2015-10-17 01:10:32:830719,11,30,-4.0,0.0,0.009 +2015-10-17 01:10:32:862369,11,31,-4.0,0.5,0.009 +2015-10-17 01:10:32:893908,11,32,-4.0,1.0,0.008 +2015-10-17 01:10:32:925395,11,33,-4.0,1.5,0.008 +2015-10-17 01:10:32:959892,11,34,-4.0,2.0,0.007 +2015-10-17 01:10:32:993308,11,35,-4.0,2.5,0.007 +2015-10-17 01:10:33:028022,11,36,-4.0,3.0,0.006 +2015-10-17 01:10:33:064693,11,37,-4.0,3.5,0.005 +2015-10-17 01:10:33:096646,11,38,-4.0,4.0,0.005 +2015-10-17 01:10:33:127849,11,39,-4.0,4.5,0.004 +2015-10-17 01:10:33:159533,11,40,-4.0,5.0,0.004 +2015-10-17 01:10:33:191968,11,41,-4.0,5.5,0.004 +2015-10-17 01:10:33:226402,11,42,-4.0,6.0,0.005 +2015-10-17 01:10:33:226402,11,42,-4.0,6.0,0.005 +2015-10-17 01:10:33:259754,11,43,-4.0,6.5,0.005 +2015-10-17 01:10:33:292002,11,44,-4.0,7.0,0.006 +2015-10-17 01:10:33:324573,11,45,-4.0,7.5,0.007 +2015-10-17 01:10:33:359937,11,46,-4.0,8.0,0.007 +2015-10-17 01:10:33:391028,11,47,-4.0,8.5,0.008 +2015-10-17 01:10:33:423469,11,48,-4.0,9.0,0.008 +2015-10-17 01:10:33:458014,11,49,-4.0,9.5,0.009 +2015-10-17 01:10:33:494056,11,50,-4.0,10.0,0.009 +2015-10-17 01:10:33:529954,11,51,-4.0,10.5,0.009 +2015-10-17 01:10:33:564435,11,52,-4.0,11.0,0.008 +2015-10-17 01:10:33:598114,11,53,-4.0,11.5,0.008 +2015-10-17 01:10:33:631310,11,54,-4.0,12.0,0.007 +2015-10-17 01:10:33:662342,11,55,-4.0,12.5,0.007 +2015-10-17 01:10:33:697767,11,56,-4.0,13.0,0.006 +2015-10-17 01:10:33:729057,11,57,-4.0,13.5,0.005 +2015-10-17 01:10:33:764554,11,58,-4.0,14.0,0.005 +2015-10-17 01:10:33:796096,11,59,-4.0,14.5,0.004 +2015-10-17 01:10:33:796096,11,59,-4.0,14.5,0.004 +2015-10-17 01:10:34:801357,12,0,-3.0,-15.0,0.005 +2015-10-17 01:10:34:833013,12,1,-3.0,-14.5,0.005 +2015-10-17 01:10:34:868597,12,2,-3.0,-14.0,0.006 +2015-10-17 01:10:34:903122,12,3,-3.0,-13.5,0.007 +2015-10-17 01:10:34:937884,12,4,-3.0,-13.0,0.008 +2015-10-17 01:10:34:969113,12,5,-3.0,-12.5,0.009 +2015-10-17 01:10:35:001444,12,6,-3.0,-12.0,0.01 +2015-10-17 01:10:35:033598,12,7,-3.0,-11.5,0.012 +2015-10-17 01:10:35:068485,12,8,-3.0,-11.0,0.013 +2015-10-17 01:10:35:099317,12,9,-3.0,-10.5,0.014 +2015-10-17 01:10:35:133876,12,10,-3.0,-10.0,0.014 +2015-10-17 01:10:35:166939,12,11,-3.0,-9.5,0.014 +2015-10-17 01:10:35:203537,12,12,-3.0,-9.0,0.013 +2015-10-17 01:10:35:236236,12,13,-3.0,-8.5,0.012 +2015-10-17 01:10:35:269120,12,14,-3.0,-8.0,0.01 +2015-10-17 01:10:35:301476,12,15,-3.0,-7.5,0.009 +2015-10-17 01:10:35:334893,12,16,-3.0,-7.0,0.008 +2015-10-17 01:10:35:368570,12,17,-3.0,-6.5,0.007 +2015-10-17 01:10:35:401884,12,18,-3.0,-6.0,0.006 +2015-10-17 01:10:35:434940,12,19,-3.0,-5.5,0.005 +2015-10-17 01:10:35:434940,12,19,-3.0,-5.5,0.005 +2015-10-17 01:10:35:467225,12,20,-3.0,-5.0,0.005 +2015-10-17 01:10:35:500515,12,21,-3.0,-4.5,0.005 +2015-10-17 01:10:35:533066,12,22,-3.0,-4.0,0.006 +2015-10-17 01:10:35:566415,12,23,-3.0,-3.5,0.007 +2015-10-17 01:10:35:599512,12,24,-3.0,-3.0,0.008 +2015-10-17 01:10:35:632764,12,25,-3.0,-2.5,0.009 +2015-10-17 01:10:35:668293,12,26,-3.0,-2.0,0.01 +2015-10-17 01:10:35:704041,12,27,-3.0,-1.5,0.012 +2015-10-17 01:10:35:735827,12,28,-3.0,-1.0,0.013 +2015-10-17 01:10:35:767678,12,29,-3.0,-0.5,0.014 +2015-10-17 01:10:35:801069,12,30,-3.0,0.0,0.014 +2015-10-17 01:10:35:835052,12,31,-3.0,0.5,0.014 +2015-10-17 01:10:35:869602,12,32,-3.0,1.0,0.013 +2015-10-17 01:10:35:905255,12,33,-3.0,1.5,0.012 +2015-10-17 01:10:35:937313,12,34,-3.0,2.0,0.01 +2015-10-17 01:10:35:969789,12,35,-3.0,2.5,0.009 +2015-10-17 01:10:36:002821,12,36,-3.0,3.0,0.008 +2015-10-17 01:10:36:039253,12,37,-3.0,3.5,0.007 +2015-10-17 01:10:36:074871,12,38,-3.0,4.0,0.006 +2015-10-17 01:10:36:111777,12,39,-3.0,4.5,0.005 +2015-10-17 01:10:36:147072,12,40,-3.0,5.0,0.005 +2015-10-17 01:10:36:178781,12,41,-3.0,5.5,0.005 +2015-10-17 01:10:36:213706,12,42,-3.0,6.0,0.006 +2015-10-17 01:10:36:247251,12,43,-3.0,6.5,0.007 +2015-10-17 01:10:36:279905,12,44,-3.0,7.0,0.008 +2015-10-17 01:10:36:314815,12,45,-3.0,7.5,0.009 +2015-10-17 01:10:36:346770,12,46,-3.0,8.0,0.01 +2015-10-17 01:10:36:380148,12,47,-3.0,8.5,0.012 +2015-10-17 01:10:36:413682,12,48,-3.0,9.0,0.013 +2015-10-17 01:10:36:449233,12,49,-3.0,9.5,0.014 +2015-10-17 01:10:36:449233,12,49,-3.0,9.5,0.014 +2015-10-17 01:10:36:482006,12,50,-3.0,10.0,0.014 +2015-10-17 01:10:36:514250,12,51,-3.0,10.5,0.014 +2015-10-17 01:10:36:547241,12,52,-3.0,11.0,0.013 +2015-10-17 01:10:36:578618,12,53,-3.0,11.5,0.012 +2015-10-17 01:10:36:610677,12,54,-3.0,12.0,0.01 +2015-10-17 01:10:36:643942,12,55,-3.0,12.5,0.009 +2015-10-17 01:10:36:675134,12,56,-3.0,13.0,0.008 +2015-10-17 01:10:36:708962,12,57,-3.0,13.5,0.007 +2015-10-17 01:10:36:745213,12,58,-3.0,14.0,0.006 +2015-10-17 01:10:36:779664,12,59,-3.0,14.5,0.005 +2015-10-17 01:10:36:779664,12,59,-3.0,14.5,0.005 +2015-10-17 01:10:37:782602,13,0,-2.0,-15.0,0.005 +2015-10-17 01:10:37:817903,13,1,-2.0,-14.5,0.006 +2015-10-17 01:10:37:852603,13,2,-2.0,-14.0,0.007 +2015-10-17 01:10:37:884308,13,3,-2.0,-13.5,0.009 +2015-10-17 01:10:37:916651,13,4,-2.0,-13.0,0.01 +2015-10-17 01:10:37:948474,13,5,-2.0,-12.5,0.013 +2015-10-17 01:10:37:980715,13,6,-2.0,-12.0,0.016 +2015-10-17 01:10:38:012821,13,7,-2.0,-11.5,0.019 +2015-10-17 01:10:38:044653,13,8,-2.0,-11.0,0.023 +2015-10-17 01:10:38:076797,13,9,-2.0,-10.5,0.026 +2015-10-17 01:10:38:108714,13,10,-2.0,-10.0,0.027 +2015-10-17 01:10:38:140965,13,11,-2.0,-9.5,0.026 +2015-10-17 01:10:38:172771,13,12,-2.0,-9.0,0.023 +2015-10-17 01:10:38:204737,13,13,-2.0,-8.5,0.019 +2015-10-17 01:10:38:236901,13,14,-2.0,-8.0,0.016 +2015-10-17 01:10:38:268703,13,15,-2.0,-7.5,0.013 +2015-10-17 01:10:38:300949,13,16,-2.0,-7.0,0.01 +2015-10-17 01:10:38:332737,13,17,-2.0,-6.5,0.009 +2015-10-17 01:10:38:368612,13,18,-2.0,-6.0,0.007 +2015-10-17 01:10:38:405154,13,19,-2.0,-5.5,0.006 +2015-10-17 01:10:38:436954,13,20,-2.0,-5.0,0.005 +2015-10-17 01:10:38:469202,13,21,-2.0,-4.5,0.006 +2015-10-17 01:10:38:501054,13,22,-2.0,-4.0,0.007 +2015-10-17 01:10:38:533250,13,23,-2.0,-3.5,0.009 +2015-10-17 01:10:38:565171,13,24,-2.0,-3.0,0.01 +2015-10-17 01:10:38:597043,13,25,-2.0,-2.5,0.013 +2015-10-17 01:10:38:597043,13,25,-2.0,-2.5,0.013 +2015-10-17 01:10:38:629184,13,26,-2.0,-2.0,0.016 +2015-10-17 01:10:38:661294,13,27,-2.0,-1.5,0.019 +2015-10-17 01:10:38:692835,13,28,-2.0,-1.0,0.023 +2015-10-17 01:10:38:725274,13,29,-2.0,-0.5,0.026 +2015-10-17 01:10:38:757241,13,30,-2.0,0.0,0.027 +2015-10-17 01:10:38:791167,13,31,-2.0,0.5,0.026 +2015-10-17 01:10:38:827091,13,32,-2.0,1.0,0.023 +2015-10-17 01:10:38:861567,13,33,-2.0,1.5,0.019 +2015-10-17 01:10:38:893717,13,34,-2.0,2.0,0.016 +2015-10-17 01:10:38:925250,13,35,-2.0,2.5,0.013 +2015-10-17 01:10:38:957484,13,36,-2.0,3.0,0.01 +2015-10-17 01:10:38:989346,13,37,-2.0,3.5,0.009 +2015-10-17 01:10:39:023374,13,38,-2.0,4.0,0.007 +2015-10-17 01:10:39:059324,13,39,-2.0,4.5,0.006 +2015-10-17 01:10:39:096909,13,40,-2.0,5.0,0.005 +2015-10-17 01:10:39:133291,13,41,-2.0,5.5,0.006 +2015-10-17 01:10:39:168127,13,42,-2.0,6.0,0.007 +2015-10-17 01:10:39:204848,13,43,-2.0,6.5,0.009 +2015-10-17 01:10:39:237581,13,44,-2.0,7.0,0.01 +2015-10-17 01:10:39:269603,13,45,-2.0,7.5,0.013 +2015-10-17 01:10:39:301821,13,46,-2.0,8.0,0.016 +2015-10-17 01:10:39:333904,13,47,-2.0,8.5,0.019 +2015-10-17 01:10:39:365796,13,48,-2.0,9.0,0.023 +2015-10-17 01:10:39:400213,13,49,-2.0,9.5,0.026 +2015-10-17 01:10:39:436944,13,50,-2.0,10.0,0.027 +2015-10-17 01:10:39:470586,13,51,-2.0,10.5,0.026 +2015-10-17 01:10:39:502039,13,52,-2.0,11.0,0.023 +2015-10-17 01:10:39:534007,13,53,-2.0,11.5,0.019 +2015-10-17 01:10:39:565876,13,54,-2.0,12.0,0.016 +2015-10-17 01:10:39:598040,13,55,-2.0,12.5,0.013 +2015-10-17 01:10:39:598040,13,55,-2.0,12.5,0.013 +2015-10-17 01:10:39:629873,13,56,-2.0,13.0,0.01 +2015-10-17 01:10:39:662106,13,57,-2.0,13.5,0.009 +2015-10-17 01:10:39:693855,13,58,-2.0,14.0,0.007 +2015-10-17 01:10:39:726277,13,59,-2.0,14.5,0.006 +2015-10-17 01:10:40:732449,14,0,-1.0,-15.0,0.006 +2015-10-17 01:10:40:732449,14,0,-1.0,-15.0,0.006 +2015-10-17 01:10:40:769215,14,1,-1.0,-14.5,0.007 +2015-10-17 01:10:40:805330,14,2,-1.0,-14.0,0.008 +2015-10-17 01:10:40:839536,14,3,-1.0,-13.5,0.01 +2015-10-17 01:10:40:871268,14,4,-1.0,-13.0,0.013 +2015-10-17 01:10:40:903439,14,5,-1.0,-12.5,0.017 +2015-10-17 01:10:40:939642,14,6,-1.0,-12.0,0.023 +2015-10-17 01:10:40:975445,14,7,-1.0,-11.5,0.031 +2015-10-17 01:10:41:007307,14,8,-1.0,-11.0,0.044 +2015-10-17 01:10:41:039232,14,9,-1.0,-10.5,0.057 +2015-10-17 01:10:41:071356,14,10,-1.0,-10.0,0.064 +2015-10-17 01:10:41:103454,14,11,-1.0,-9.5,0.057 +2015-10-17 01:10:41:139194,14,12,-1.0,-9.0,0.044 +2015-10-17 01:10:41:175443,14,13,-1.0,-8.5,0.031 +2015-10-17 01:10:41:207636,14,14,-1.0,-8.0,0.023 +2015-10-17 01:10:41:239454,14,15,-1.0,-7.5,0.017 +2015-10-17 01:10:41:271622,14,16,-1.0,-7.0,0.013 +2015-10-17 01:10:41:303567,14,17,-1.0,-6.5,0.01 +2015-10-17 01:10:41:335670,14,18,-1.0,-6.0,0.008 +2015-10-17 01:10:41:367882,14,19,-1.0,-5.5,0.007 +2015-10-17 01:10:41:399720,14,20,-1.0,-5.0,0.006 +2015-10-17 01:10:41:431651,14,21,-1.0,-4.5,0.007 +2015-10-17 01:10:41:463999,14,22,-1.0,-4.0,0.008 +2015-10-17 01:10:41:495840,14,23,-1.0,-3.5,0.01 +2015-10-17 01:10:41:527900,14,24,-1.0,-3.0,0.013 +2015-10-17 01:10:41:559802,14,25,-1.0,-2.5,0.017 +2015-10-17 01:10:41:591746,14,26,-1.0,-2.0,0.023 +2015-10-17 01:10:41:623960,14,27,-1.0,-1.5,0.031 +2015-10-17 01:10:41:655740,14,28,-1.0,-1.0,0.044 +2015-10-17 01:10:41:688025,14,29,-1.0,-0.5,0.057 +2015-10-17 01:10:41:719896,14,30,-1.0,0.0,0.064 +2015-10-17 01:10:41:752234,14,31,-1.0,0.5,0.057 +2015-10-17 01:10:41:752234,14,31,-1.0,0.5,0.057 +2015-10-17 01:10:41:788094,14,32,-1.0,1.0,0.044 +2015-10-17 01:10:41:824142,14,33,-1.0,1.5,0.031 +2015-10-17 01:10:41:855969,14,34,-1.0,2.0,0.023 +2015-10-17 01:10:41:888292,14,35,-1.0,2.5,0.017 +2015-10-17 01:10:41:920584,14,36,-1.0,3.0,0.013 +2015-10-17 01:10:41:957020,14,37,-1.0,3.5,0.01 +2015-10-17 01:10:41:992257,14,38,-1.0,4.0,0.008 +2015-10-17 01:10:42:024410,14,39,-1.0,4.5,0.007 +2015-10-17 01:10:42:056225,14,40,-1.0,5.0,0.006 +2015-10-17 01:10:42:088406,14,41,-1.0,5.5,0.007 +2015-10-17 01:10:42:120235,14,42,-1.0,6.0,0.008 +2015-10-17 01:10:42:152442,14,43,-1.0,6.5,0.01 +2015-10-17 01:10:42:184607,14,44,-1.0,7.0,0.013 +2015-10-17 01:10:42:216432,14,45,-1.0,7.5,0.017 +2015-10-17 01:10:42:248661,14,46,-1.0,8.0,0.023 +2015-10-17 01:10:42:280524,14,47,-1.0,8.5,0.031 +2015-10-17 01:10:42:311918,14,48,-1.0,9.0,0.044 +2015-10-17 01:10:42:344458,14,49,-1.0,9.5,0.057 +2015-10-17 01:10:42:376569,14,50,-1.0,10.0,0.064 +2015-10-17 01:10:42:408761,14,51,-1.0,10.5,0.057 +2015-10-17 01:10:42:440593,14,52,-1.0,11.0,0.044 +2015-10-17 01:10:42:475944,14,53,-1.0,11.5,0.031 +2015-10-17 01:10:42:512583,14,54,-1.0,12.0,0.023 +2015-10-17 01:10:42:544975,14,55,-1.0,12.5,0.017 +2015-10-17 01:10:42:576730,14,56,-1.0,13.0,0.013 +2015-10-17 01:10:42:608565,14,57,-1.0,13.5,0.01 +2015-10-17 01:10:42:640772,14,58,-1.0,14.0,0.008 +2015-10-17 01:10:42:672808,14,59,-1.0,14.5,0.007 +2015-10-17 01:10:42:672808,14,59,-1.0,14.5,0.007 +2015-10-17 01:10:43:674196,15,0,0.0,-15.0,0.006 +2015-10-17 01:10:43:705178,15,1,0.0,-14.5,0.007 +2015-10-17 01:10:43:737643,15,2,0.0,-14.0,0.009 +2015-10-17 01:10:43:769496,15,3,0.0,-13.5,0.011 +2015-10-17 01:10:43:802409,15,4,0.0,-13.0,0.014 +2015-10-17 01:10:43:833887,15,5,0.0,-12.5,0.019 +2015-10-17 01:10:43:865884,15,6,0.0,-12.0,0.027 +2015-10-17 01:10:43:897732,15,7,0.0,-11.5,0.04 +2015-10-17 01:10:43:897732,15,7,0.0,-11.5,0.04 +2015-10-17 01:10:43:929867,15,8,0.0,-11.0,0.064 +2015-10-17 01:10:43:961839,15,9,0.0,-10.5,0.099 +2015-10-17 01:10:43:994016,15,10,0.0,-10.0,0.117 +2015-10-17 01:10:44:026173,15,11,0.0,-9.5,0.099 +2015-10-17 01:10:44:057846,15,12,0.0,-9.0,0.064 +2015-10-17 01:10:44:090152,15,13,0.0,-8.5,0.04 +2015-10-17 01:10:44:121995,15,14,0.0,-8.0,0.027 +2015-10-17 01:10:44:157979,15,15,0.0,-7.5,0.019 +2015-10-17 01:10:44:194019,15,16,0.0,-7.0,0.014 +2015-10-17 01:10:44:226181,15,17,0.0,-6.5,0.011 +2015-10-17 01:10:44:258646,15,18,0.0,-6.0,0.009 +2015-10-17 01:10:44:292891,15,19,0.0,-5.5,0.007 +2015-10-17 01:10:44:329597,15,20,0.0,-5.0,0.006 +2015-10-17 01:10:44:365674,15,21,0.0,-4.5,0.007 +2015-10-17 01:10:44:402286,15,22,0.0,-4.0,0.009 +2015-10-17 01:10:44:434272,15,23,0.0,-3.5,0.011 +2015-10-17 01:10:44:466428,15,24,0.0,-3.0,0.014 +2015-10-17 01:10:44:498276,15,25,0.0,-2.5,0.019 +2015-10-17 01:10:44:530819,15,26,0.0,-2.0,0.027 +2015-10-17 01:10:44:562542,15,27,0.0,-1.5,0.04 +2015-10-17 01:10:44:594489,15,28,0.0,-1.0,0.064 +2015-10-17 01:10:44:626714,15,29,0.0,-0.5,0.099 +2015-10-17 01:10:44:658497,15,30,0.0,0.0,0.117 +2015-10-17 01:10:44:690395,15,31,0.0,0.5,0.099 +2015-10-17 01:10:44:722596,15,32,0.0,1.0,0.064 +2015-10-17 01:10:44:754770,15,33,0.0,1.5,0.04 +2015-10-17 01:10:44:788716,15,34,0.0,2.0,0.027 +2015-10-17 01:10:44:820347,15,35,0.0,2.5,0.019 +2015-10-17 01:10:44:852385,15,36,0.0,3.0,0.014 +2015-10-17 01:10:44:888365,15,37,0.0,3.5,0.011 +2015-10-17 01:10:44:921977,15,38,0.0,4.0,0.009 +2015-10-17 01:10:44:921977,15,38,0.0,4.0,0.009 +2015-10-17 01:10:44:954711,15,39,0.0,4.5,0.007 +2015-10-17 01:10:44:986749,15,40,0.0,5.0,0.006 +2015-10-17 01:10:45:019608,15,41,0.0,5.5,0.007 +2015-10-17 01:10:45:054430,15,42,0.0,6.0,0.009 +2015-10-17 01:10:45:090886,15,43,0.0,6.5,0.011 +2015-10-17 01:10:45:122782,15,44,0.0,7.0,0.014 +2015-10-17 01:10:45:154849,15,45,0.0,7.5,0.019 +2015-10-17 01:10:45:187052,15,46,0.0,8.0,0.027 +2015-10-17 01:10:45:219120,15,47,0.0,8.5,0.04 +2015-10-17 01:10:45:250880,15,48,0.0,9.0,0.064 +2015-10-17 01:10:45:282932,15,49,0.0,9.5,0.099 +2015-10-17 01:10:45:315042,15,50,0.0,10.0,0.117 +2015-10-17 01:10:45:347210,15,51,0.0,10.5,0.099 +2015-10-17 01:10:45:379076,15,52,0.0,11.0,0.064 +2015-10-17 01:10:45:412034,15,53,0.0,11.5,0.04 +2015-10-17 01:10:45:443453,15,54,0.0,12.0,0.027 +2015-10-17 01:10:45:475384,15,55,0.0,12.5,0.019 +2015-10-17 01:10:45:507111,15,56,0.0,13.0,0.014 +2015-10-17 01:10:45:539206,15,57,0.0,13.5,0.011 +2015-10-17 01:10:45:575201,15,58,0.0,14.0,0.009 +2015-10-17 01:10:45:611443,15,59,0.0,14.5,0.007 +2015-10-17 01:10:45:611443,15,59,0.0,14.5,0.007 +2015-10-17 01:10:46:618067,16,0,1.0,-15.0,0.006 +2015-10-17 01:10:46:652265,16,1,1.0,-14.5,0.007 +2015-10-17 01:10:46:684138,16,2,1.0,-14.0,0.008 +2015-10-17 01:10:46:716399,16,3,1.0,-13.5,0.01 +2015-10-17 01:10:46:748485,16,4,1.0,-13.0,0.013 +2015-10-17 01:10:46:780415,16,5,1.0,-12.5,0.017 +2015-10-17 01:10:46:812238,16,6,1.0,-12.0,0.023 +2015-10-17 01:10:46:844230,16,7,1.0,-11.5,0.031 +2015-10-17 01:10:46:876321,16,8,1.0,-11.0,0.044 +2015-10-17 01:10:46:908529,16,9,1.0,-10.5,0.057 +2015-10-17 01:10:46:940217,16,10,1.0,-10.0,0.064 +2015-10-17 01:10:46:972378,16,11,1.0,-9.5,0.057 +2015-10-17 01:10:47:004527,16,12,1.0,-9.0,0.044 +2015-10-17 01:10:47:036065,16,13,1.0,-8.5,0.031 +2015-10-17 01:10:47:036065,16,13,1.0,-8.5,0.031 +2015-10-17 01:10:47:068387,16,14,1.0,-8.0,0.023 +2015-10-17 01:10:47:105150,16,15,1.0,-7.5,0.017 +2015-10-17 01:10:47:140568,16,16,1.0,-7.0,0.013 +2015-10-17 01:10:47:174319,16,17,1.0,-6.5,0.01 +2015-10-17 01:10:47:205956,16,18,1.0,-6.0,0.008 +2015-10-17 01:10:47:241952,16,19,1.0,-5.5,0.007 +2015-10-17 01:10:47:276796,16,20,1.0,-5.0,0.006 +2015-10-17 01:10:47:313184,16,21,1.0,-4.5,0.007 +2015-10-17 01:10:47:348584,16,22,1.0,-4.0,0.008 +2015-10-17 01:10:47:380592,16,23,1.0,-3.5,0.01 +2015-10-17 01:10:47:412661,16,24,1.0,-3.0,0.013 +2015-10-17 01:10:47:444385,16,25,1.0,-2.5,0.017 +2015-10-17 01:10:47:477230,16,26,1.0,-2.0,0.023 +2015-10-17 01:10:47:509341,16,27,1.0,-1.5,0.031 +2015-10-17 01:10:47:541054,16,28,1.0,-1.0,0.044 +2015-10-17 01:10:47:571718,16,29,1.0,-0.5,0.057 +2015-10-17 01:10:47:604803,16,30,1.0,0.0,0.064 +2015-10-17 01:10:47:636337,16,31,1.0,0.5,0.057 +2015-10-17 01:10:47:669089,16,32,1.0,1.0,0.044 +2015-10-17 01:10:47:700753,16,33,1.0,1.5,0.031 +2015-10-17 01:10:47:732645,16,34,1.0,2.0,0.023 +2015-10-17 01:10:47:764960,16,35,1.0,2.5,0.017 +2015-10-17 01:10:47:796511,16,36,1.0,3.0,0.013 +2015-10-17 01:10:47:833093,16,37,1.0,3.5,0.01 +2015-10-17 01:10:47:868475,16,38,1.0,4.0,0.008 +2015-10-17 01:10:47:901144,16,39,1.0,4.5,0.007 +2015-10-17 01:10:47:932982,16,40,1.0,5.0,0.006 +2015-10-17 01:10:47:965298,16,41,1.0,5.5,0.007 +2015-10-17 01:10:48:001409,16,42,1.0,6.0,0.008 +2015-10-17 01:10:48:033205,16,43,1.0,6.5,0.01 +2015-10-17 01:10:48:068000,16,44,1.0,7.0,0.013 +2015-10-17 01:10:48:068000,16,44,1.0,7.0,0.013 +2015-10-17 01:10:48:099689,16,45,1.0,7.5,0.017 +2015-10-17 01:10:48:133055,16,46,1.0,8.0,0.023 +2015-10-17 01:10:48:165841,16,47,1.0,8.5,0.031 +2015-10-17 01:10:48:199018,16,48,1.0,9.0,0.044 +2015-10-17 01:10:48:232399,16,49,1.0,9.5,0.057 +2015-10-17 01:10:48:268852,16,50,1.0,10.0,0.064 +2015-10-17 01:10:48:303420,16,51,1.0,10.5,0.057 +2015-10-17 01:10:48:339155,16,52,1.0,11.0,0.044 +2015-10-17 01:10:48:373513,16,53,1.0,11.5,0.031 +2015-10-17 01:10:48:405915,16,54,1.0,12.0,0.023 +2015-10-17 01:10:48:437756,16,55,1.0,12.5,0.017 +2015-10-17 01:10:48:469466,16,56,1.0,13.0,0.013 +2015-10-17 01:10:48:503485,16,57,1.0,13.5,0.01 +2015-10-17 01:10:48:539446,16,58,1.0,14.0,0.008 +2015-10-17 01:10:48:573648,16,59,1.0,14.5,0.007 +2015-10-17 01:10:48:573648,16,59,1.0,14.5,0.007 +2015-10-17 01:10:49:579612,17,0,2.0,-15.0,0.005 +2015-10-17 01:10:49:614300,17,1,2.0,-14.5,0.006 +2015-10-17 01:10:49:648287,17,2,2.0,-14.0,0.007 +2015-10-17 01:10:49:679218,17,3,2.0,-13.5,0.009 +2015-10-17 01:10:49:716521,17,4,2.0,-13.0,0.01 +2015-10-17 01:10:49:752469,17,5,2.0,-12.5,0.013 +2015-10-17 01:10:49:788368,17,6,2.0,-12.0,0.016 +2015-10-17 01:10:49:822220,17,7,2.0,-11.5,0.019 +2015-10-17 01:10:49:854557,17,8,2.0,-11.0,0.023 +2015-10-17 01:10:49:886535,17,9,2.0,-10.5,0.026 +2015-10-17 01:10:49:918487,17,10,2.0,-10.0,0.027 +2015-10-17 01:10:49:950747,17,11,2.0,-9.5,0.026 +2015-10-17 01:10:49:982810,17,12,2.0,-9.0,0.023 +2015-10-17 01:10:50:014878,17,13,2.0,-8.5,0.019 +2015-10-17 01:10:50:046964,17,14,2.0,-8.0,0.016 +2015-10-17 01:10:50:079993,17,15,2.0,-7.5,0.013 +2015-10-17 01:10:50:111543,17,16,2.0,-7.0,0.01 +2015-10-17 01:10:50:143337,17,17,2.0,-6.5,0.009 +2015-10-17 01:10:50:174979,17,18,2.0,-6.0,0.007 +2015-10-17 01:10:50:210675,17,19,2.0,-5.5,0.006 +2015-10-17 01:10:50:210675,17,19,2.0,-5.5,0.006 +2015-10-17 01:10:50:246665,17,20,2.0,-5.0,0.005 +2015-10-17 01:10:50:279098,17,21,2.0,-4.5,0.006 +2015-10-17 01:10:50:311641,17,22,2.0,-4.0,0.007 +2015-10-17 01:10:50:346414,17,23,2.0,-3.5,0.009 +2015-10-17 01:10:50:377322,17,24,2.0,-3.0,0.01 +2015-10-17 01:10:50:412656,17,25,2.0,-2.5,0.013 +2015-10-17 01:10:50:447001,17,26,2.0,-2.0,0.016 +2015-10-17 01:10:50:479275,17,27,2.0,-1.5,0.019 +2015-10-17 01:10:50:512964,17,28,2.0,-1.0,0.023 +2015-10-17 01:10:50:544241,17,29,2.0,-0.5,0.026 +2015-10-17 01:10:50:575797,17,30,2.0,0.0,0.027 +2015-10-17 01:10:50:607256,17,31,2.0,0.5,0.026 +2015-10-17 01:10:50:638767,17,32,2.0,1.0,0.023 +2015-10-17 01:10:50:670021,17,33,2.0,1.5,0.019 +2015-10-17 01:10:50:702297,17,34,2.0,2.0,0.016 +2015-10-17 01:10:50:733879,17,35,2.0,2.5,0.013 +2015-10-17 01:10:50:765960,17,36,2.0,3.0,0.01 +2015-10-17 01:10:50:797875,17,37,2.0,3.5,0.009 +2015-10-17 01:10:50:833493,17,38,2.0,4.0,0.007 +2015-10-17 01:10:50:870102,17,39,2.0,4.5,0.006 +2015-10-17 01:10:50:902061,17,40,2.0,5.0,0.005 +2015-10-17 01:10:50:934057,17,41,2.0,5.5,0.006 +2015-10-17 01:10:50:965827,17,42,2.0,6.0,0.007 +2015-10-17 01:10:50:997808,17,43,2.0,6.5,0.009 +2015-10-17 01:10:51:030209,17,44,2.0,7.0,0.01 +2015-10-17 01:10:51:064831,17,45,2.0,7.5,0.013 +2015-10-17 01:10:51:098244,17,46,2.0,8.0,0.016 +2015-10-17 01:10:51:130266,17,47,2.0,8.5,0.019 +2015-10-17 01:10:51:162065,17,48,2.0,9.0,0.023 +2015-10-17 01:10:51:194425,17,49,2.0,9.5,0.026 +2015-10-17 01:10:51:226624,17,50,2.0,10.0,0.027 +2015-10-17 01:10:51:226624,17,50,2.0,10.0,0.027 +2015-10-17 01:10:51:258378,17,51,2.0,10.5,0.026 +2015-10-17 01:10:51:290278,17,52,2.0,11.0,0.023 +2015-10-17 01:10:51:322402,17,53,2.0,11.5,0.019 +2015-10-17 01:10:51:354891,17,54,2.0,12.0,0.016 +2015-10-17 01:10:51:386249,17,55,2.0,12.5,0.013 +2015-10-17 01:10:51:418632,17,56,2.0,13.0,0.01 +2015-10-17 01:10:51:450560,17,57,2.0,13.5,0.009 +2015-10-17 01:10:51:482558,17,58,2.0,14.0,0.007 +2015-10-17 01:10:51:514369,17,59,2.0,14.5,0.006 +2015-10-17 01:10:51:514369,17,59,2.0,14.5,0.006 +2015-10-17 01:10:52:516889,18,0,3.0,-15.0,0.005 +2015-10-17 01:10:52:550884,18,1,3.0,-14.5,0.005 +2015-10-17 01:10:52:586916,18,2,3.0,-14.0,0.006 +2015-10-17 01:10:52:620655,18,3,3.0,-13.5,0.007 +2015-10-17 01:10:52:655002,18,4,3.0,-13.0,0.008 +2015-10-17 01:10:52:691775,18,5,3.0,-12.5,0.009 +2015-10-17 01:10:52:724189,18,6,3.0,-12.0,0.01 +2015-10-17 01:10:52:755669,18,7,3.0,-11.5,0.012 +2015-10-17 01:10:52:788121,18,8,3.0,-11.0,0.013 +2015-10-17 01:10:52:820401,18,9,3.0,-10.5,0.014 +2015-10-17 01:10:52:855171,18,10,3.0,-10.0,0.014 +2015-10-17 01:10:52:891846,18,11,3.0,-9.5,0.014 +2015-10-17 01:10:52:927359,18,12,3.0,-9.0,0.013 +2015-10-17 01:10:52:960741,18,13,3.0,-8.5,0.012 +2015-10-17 01:10:52:996452,18,14,3.0,-8.0,0.01 +2015-10-17 01:10:53:028383,18,15,3.0,-7.5,0.009 +2015-10-17 01:10:53:060338,18,16,3.0,-7.0,0.008 +2015-10-17 01:10:53:092582,18,17,3.0,-6.5,0.007 +2015-10-17 01:10:53:124500,18,18,3.0,-6.0,0.006 +2015-10-17 01:10:53:158837,18,19,3.0,-5.5,0.005 +2015-10-17 01:10:53:195554,18,20,3.0,-5.0,0.005 +2015-10-17 01:10:53:228610,18,21,3.0,-4.5,0.005 +2015-10-17 01:10:53:260507,18,22,3.0,-4.0,0.006 +2015-10-17 01:10:53:292701,18,23,3.0,-3.5,0.007 +2015-10-17 01:10:53:324687,18,24,3.0,-3.0,0.008 +2015-10-17 01:10:53:324687,18,24,3.0,-3.0,0.008 +2015-10-17 01:10:53:356698,18,25,3.0,-2.5,0.009 +2015-10-17 01:10:53:388744,18,26,3.0,-2.0,0.01 +2015-10-17 01:10:53:420715,18,27,3.0,-1.5,0.012 +2015-10-17 01:10:53:452862,18,28,3.0,-1.0,0.013 +2015-10-17 01:10:53:484281,18,29,3.0,-0.5,0.014 +2015-10-17 01:10:53:515794,18,30,3.0,0.0,0.014 +2015-10-17 01:10:53:553133,18,31,3.0,0.5,0.014 +2015-10-17 01:10:53:588871,18,32,3.0,1.0,0.013 +2015-10-17 01:10:53:624773,18,33,3.0,1.5,0.012 +2015-10-17 01:10:53:660958,18,34,3.0,2.0,0.01 +2015-10-17 01:10:53:694848,18,35,3.0,2.5,0.009 +2015-10-17 01:10:53:730788,18,36,3.0,3.0,0.008 +2015-10-17 01:10:53:767060,18,37,3.0,3.5,0.007 +2015-10-17 01:10:53:803818,18,38,3.0,4.0,0.006 +2015-10-17 01:10:53:836988,18,39,3.0,4.5,0.005 +2015-10-17 01:10:53:868731,18,40,3.0,5.0,0.005 +2015-10-17 01:10:53:900976,18,41,3.0,5.5,0.005 +2015-10-17 01:10:53:933003,18,42,3.0,6.0,0.006 +2015-10-17 01:10:53:964657,18,43,3.0,6.5,0.007 +2015-10-17 01:10:53:997256,18,44,3.0,7.0,0.008 +2015-10-17 01:10:54:029396,18,45,3.0,7.5,0.009 +2015-10-17 01:10:54:062899,18,46,3.0,8.0,0.01 +2015-10-17 01:10:54:094614,18,47,3.0,8.5,0.012 +2015-10-17 01:10:54:126909,18,48,3.0,9.0,0.013 +2015-10-17 01:10:54:158845,18,49,3.0,9.5,0.014 +2015-10-17 01:10:54:191154,18,50,3.0,10.0,0.014 +2015-10-17 01:10:54:222688,18,51,3.0,10.5,0.014 +2015-10-17 01:10:54:255124,18,52,3.0,11.0,0.013 +2015-10-17 01:10:54:287487,18,53,3.0,11.5,0.012 +2015-10-17 01:10:54:320997,18,54,3.0,12.0,0.01 +2015-10-17 01:10:54:353688,18,55,3.0,12.5,0.009 +2015-10-17 01:10:54:353688,18,55,3.0,12.5,0.009 +2015-10-17 01:10:54:387856,18,56,3.0,13.0,0.008 +2015-10-17 01:10:54:421267,18,57,3.0,13.5,0.007 +2015-10-17 01:10:54:454876,18,58,3.0,14.0,0.006 +2015-10-17 01:10:54:487015,18,59,3.0,14.5,0.005 +2015-10-17 01:10:55:491509,19,0,4.0,-15.0,0.004 +2015-10-17 01:10:55:491509,19,0,4.0,-15.0,0.004 +2015-10-17 01:10:55:525259,19,1,4.0,-14.5,0.004 +2015-10-17 01:10:55:560158,19,2,4.0,-14.0,0.005 +2015-10-17 01:10:55:592082,19,3,4.0,-13.5,0.005 +2015-10-17 01:10:55:624214,19,4,4.0,-13.0,0.006 +2015-10-17 01:10:55:655868,19,5,4.0,-12.5,0.007 +2015-10-17 01:10:55:688442,19,6,4.0,-12.0,0.007 +2015-10-17 01:10:55:720774,19,7,4.0,-11.5,0.008 +2015-10-17 01:10:55:751694,19,8,4.0,-11.0,0.008 +2015-10-17 01:10:55:784224,19,9,4.0,-10.5,0.009 +2015-10-17 01:10:55:820050,19,10,4.0,-10.0,0.009 +2015-10-17 01:10:55:856558,19,11,4.0,-9.5,0.009 +2015-10-17 01:10:55:891644,19,12,4.0,-9.0,0.008 +2015-10-17 01:10:55:928483,19,13,4.0,-8.5,0.008 +2015-10-17 01:10:55:960345,19,14,4.0,-8.0,0.007 +2015-10-17 01:10:55:992101,19,15,4.0,-7.5,0.007 +2015-10-17 01:10:56:026438,19,16,4.0,-7.0,0.006 +2015-10-17 01:10:56:062355,19,17,4.0,-6.5,0.005 +2015-10-17 01:10:56:094068,19,18,4.0,-6.0,0.005 +2015-10-17 01:10:56:128777,19,19,4.0,-5.5,0.004 +2015-10-17 01:10:56:160775,19,20,4.0,-5.0,0.004 +2015-10-17 01:10:56:192965,19,21,4.0,-4.5,0.004 +2015-10-17 01:10:56:227479,19,22,4.0,-4.0,0.005 +2015-10-17 01:10:56:264189,19,23,4.0,-3.5,0.005 +2015-10-17 01:10:56:296857,19,24,4.0,-3.0,0.006 +2015-10-17 01:10:56:328855,19,25,4.0,-2.5,0.007 +2015-10-17 01:10:56:360855,19,26,4.0,-2.0,0.007 +2015-10-17 01:10:56:397134,19,27,4.0,-1.5,0.008 +2015-10-17 01:10:56:432816,19,28,4.0,-1.0,0.008 +2015-10-17 01:10:56:464956,19,29,4.0,-0.5,0.009 +2015-10-17 01:10:56:497468,19,30,4.0,0.0,0.009 +2015-10-17 01:10:56:497468,19,30,4.0,0.0,0.009 +2015-10-17 01:10:56:529321,19,31,4.0,0.5,0.009 +2015-10-17 01:10:56:561397,19,32,4.0,1.0,0.008 +2015-10-17 01:10:56:593386,19,33,4.0,1.5,0.008 +2015-10-17 01:10:56:625089,19,34,4.0,2.0,0.007 +2015-10-17 01:10:56:659148,19,35,4.0,2.5,0.007 +2015-10-17 01:10:56:690567,19,36,4.0,3.0,0.006 +2015-10-17 01:10:56:725212,19,37,4.0,3.5,0.005 +2015-10-17 01:10:56:759211,19,38,4.0,4.0,0.005 +2015-10-17 01:10:56:793342,19,39,4.0,4.5,0.004 +2015-10-17 01:10:56:825180,19,40,4.0,5.0,0.004 +2015-10-17 01:10:56:857248,19,41,4.0,5.5,0.004 +2015-10-17 01:10:56:889178,19,42,4.0,6.0,0.005 +2015-10-17 01:10:56:921346,19,43,4.0,6.5,0.005 +2015-10-17 01:10:56:955909,19,44,4.0,7.0,0.006 +2015-10-17 01:10:56:992607,19,45,4.0,7.5,0.007 +2015-10-17 01:10:57:027002,19,46,4.0,8.0,0.007 +2015-10-17 01:10:57:065293,19,47,4.0,8.5,0.008 +2015-10-17 01:10:57:097448,19,48,4.0,9.0,0.008 +2015-10-17 01:10:57:129601,19,49,4.0,9.5,0.009 +2015-10-17 01:10:57:161665,19,50,4.0,10.0,0.009 +2015-10-17 01:10:57:193478,19,51,4.0,10.5,0.009 +2015-10-17 01:10:57:225552,19,52,4.0,11.0,0.008 +2015-10-17 01:10:57:257606,19,53,4.0,11.5,0.008 +2015-10-17 01:10:57:289724,19,54,4.0,12.0,0.007 +2015-10-17 01:10:57:321258,19,55,4.0,12.5,0.007 +2015-10-17 01:10:57:353929,19,56,4.0,13.0,0.006 +2015-10-17 01:10:57:385770,19,57,4.0,13.5,0.005 +2015-10-17 01:10:57:417684,19,58,4.0,14.0,0.005 +2015-10-17 01:10:57:453743,19,59,4.0,14.5,0.004 +2015-10-17 01:10:57:453743,19,59,4.0,14.5,0.004 +2015-10-17 01:10:58:458789,20,0,5.0,-15.0,0.003 +2015-10-17 01:10:58:492782,20,1,5.0,-14.5,0.004 +2015-10-17 01:10:58:529277,20,2,5.0,-14.0,0.004 +2015-10-17 01:10:58:562963,20,3,5.0,-13.5,0.004 +2015-10-17 01:10:58:594849,20,4,5.0,-13.0,0.005 +2015-10-17 01:10:58:626813,20,5,5.0,-12.5,0.005 +2015-10-17 01:10:58:658968,20,6,5.0,-12.0,0.005 +2015-10-17 01:10:58:658968,20,6,5.0,-12.0,0.005 +2015-10-17 01:10:58:689740,20,7,5.0,-11.5,0.006 +2015-10-17 01:10:58:722383,20,8,5.0,-11.0,0.006 +2015-10-17 01:10:58:755003,20,9,5.0,-10.5,0.006 +2015-10-17 01:10:58:790956,20,10,5.0,-10.0,0.006 +2015-10-17 01:10:58:826899,20,11,5.0,-9.5,0.006 +2015-10-17 01:10:58:859138,20,12,5.0,-9.0,0.006 +2015-10-17 01:10:58:891593,20,13,5.0,-8.5,0.006 +2015-10-17 01:10:58:927239,20,14,5.0,-8.0,0.005 +2015-10-17 01:10:58:962229,20,15,5.0,-7.5,0.005 +2015-10-17 01:10:58:995214,20,16,5.0,-7.0,0.005 +2015-10-17 01:10:59:027679,20,17,5.0,-6.5,0.004 +2015-10-17 01:10:59:063731,20,18,5.0,-6.0,0.004 +2015-10-17 01:10:59:099479,20,19,5.0,-5.5,0.004 +2015-10-17 01:10:59:131109,20,20,5.0,-5.0,0.003 +2015-10-17 01:10:59:163453,20,21,5.0,-4.5,0.004 +2015-10-17 01:10:59:195425,20,22,5.0,-4.0,0.004 +2015-10-17 01:10:59:230428,20,23,5.0,-3.5,0.004 +2015-10-17 01:10:59:267215,20,24,5.0,-3.0,0.005 +2015-10-17 01:10:59:299057,20,25,5.0,-2.5,0.005 +2015-10-17 01:10:59:331549,20,26,5.0,-2.0,0.005 +2015-10-17 01:10:59:363687,20,27,5.0,-1.5,0.006 +2015-10-17 01:10:59:395684,20,28,5.0,-1.0,0.006 +2015-10-17 01:10:59:427656,20,29,5.0,-0.5,0.006 +2015-10-17 01:10:59:459711,20,30,5.0,0.0,0.006 +2015-10-17 01:10:59:491772,20,31,5.0,0.5,0.006 +2015-10-17 01:10:59:523840,20,32,5.0,1.0,0.006 +2015-10-17 01:10:59:555701,20,33,5.0,1.5,0.006 +2015-10-17 01:10:59:587702,20,34,5.0,2.0,0.005 +2015-10-17 01:10:59:619852,20,35,5.0,2.5,0.005 +2015-10-17 01:10:59:651892,20,36,5.0,3.0,0.005 +2015-10-17 01:10:59:686075,20,37,5.0,3.5,0.004 +2015-10-17 01:10:59:686075,20,37,5.0,3.5,0.004 +2015-10-17 01:10:59:717427,20,38,5.0,4.0,0.004 +2015-10-17 01:10:59:753994,20,39,5.0,4.5,0.004 +2015-10-17 01:10:59:789613,20,40,5.0,5.0,0.003 +2015-10-17 01:10:59:822081,20,41,5.0,5.5,0.004 +2015-10-17 01:10:59:853410,20,42,5.0,6.0,0.004 +2015-10-17 01:10:59:889868,20,43,5.0,6.5,0.004 +2015-10-17 01:10:59:924153,20,44,5.0,7.0,0.005 +2015-10-17 01:10:59:955784,20,45,5.0,7.5,0.005 +2015-10-17 01:10:59:988327,20,46,5.0,8.0,0.005 +2015-10-17 01:11:00:020003,20,47,5.0,8.5,0.006 +2015-10-17 01:11:00:051965,20,48,5.0,9.0,0.006 +2015-10-17 01:11:00:084110,20,49,5.0,9.5,0.006 +2015-10-17 01:11:00:119797,20,50,5.0,10.0,0.006 +2015-10-17 01:11:00:156248,20,51,5.0,10.5,0.006 +2015-10-17 01:11:00:188386,20,52,5.0,11.0,0.006 +2015-10-17 01:11:00:220451,20,53,5.0,11.5,0.006 +2015-10-17 01:11:00:252549,20,54,5.0,12.0,0.005 +2015-10-17 01:11:00:284635,20,55,5.0,12.5,0.005 +2015-10-17 01:11:00:316570,20,56,5.0,13.0,0.005 +2015-10-17 01:11:00:348690,20,57,5.0,13.5,0.004 +2015-10-17 01:11:00:380665,20,58,5.0,14.0,0.004 +2015-10-17 01:11:00:412682,20,59,5.0,14.5,0.004 +2015-10-17 01:11:00:412682,20,59,5.0,14.5,0.004 +2015-10-17 01:11:01:419190,21,0,6.0,-15.0,0.004 +2015-10-17 01:11:01:453614,21,1,6.0,-14.5,0.004 +2015-10-17 01:11:01:485651,21,2,6.0,-14.0,0.005 +2015-10-17 01:11:01:517494,21,3,6.0,-13.5,0.005 +2015-10-17 01:11:01:549998,21,4,6.0,-13.0,0.006 +2015-10-17 01:11:01:581782,21,5,6.0,-12.5,0.007 +2015-10-17 01:11:01:613637,21,6,6.0,-12.0,0.007 +2015-10-17 01:11:01:645736,21,7,6.0,-11.5,0.008 +2015-10-17 01:11:01:677405,21,8,6.0,-11.0,0.008 +2015-10-17 01:11:01:709711,21,9,6.0,-10.5,0.009 +2015-10-17 01:11:01:741954,21,10,6.0,-10.0,0.009 +2015-10-17 01:11:01:773760,21,11,6.0,-9.5,0.009 +2015-10-17 01:11:01:805825,21,12,6.0,-9.0,0.008 +2015-10-17 01:11:01:841788,21,13,6.0,-8.5,0.008 +2015-10-17 01:11:01:841788,21,13,6.0,-8.5,0.008 +2015-10-17 01:11:01:877713,21,14,6.0,-8.0,0.007 +2015-10-17 01:11:01:909770,21,15,6.0,-7.5,0.007 +2015-10-17 01:11:01:941852,21,16,6.0,-7.0,0.006 +2015-10-17 01:11:01:973932,21,17,6.0,-6.5,0.005 +2015-10-17 01:11:02:005846,21,18,6.0,-6.0,0.005 +2015-10-17 01:11:02:040755,21,19,6.0,-5.5,0.004 +2015-10-17 01:11:02:077158,21,20,6.0,-5.0,0.004 +2015-10-17 01:11:02:112001,21,21,6.0,-4.5,0.004 +2015-10-17 01:11:02:147670,21,22,6.0,-4.0,0.005 +2015-10-17 01:11:02:182224,21,23,6.0,-3.5,0.005 +2015-10-17 01:11:02:214255,21,24,6.0,-3.0,0.006 +2015-10-17 01:11:02:246367,21,25,6.0,-2.5,0.007 +2015-10-17 01:11:02:278281,21,26,6.0,-2.0,0.007 +2015-10-17 01:11:02:310250,21,27,6.0,-1.5,0.008 +2015-10-17 01:11:02:342257,21,28,6.0,-1.0,0.008 +2015-10-17 01:11:02:373821,21,29,6.0,-0.5,0.009 +2015-10-17 01:11:02:406420,21,30,6.0,0.0,0.009 +2015-10-17 01:11:02:438348,21,31,6.0,0.5,0.009 +2015-10-17 01:11:02:470261,21,32,6.0,1.0,0.008 +2015-10-17 01:11:02:502346,21,33,6.0,1.5,0.008 +2015-10-17 01:11:02:534358,21,34,6.0,2.0,0.007 +2015-10-17 01:11:02:570005,21,35,6.0,2.5,0.007 +2015-10-17 01:11:02:601702,21,36,6.0,3.0,0.006 +2015-10-17 01:11:02:638708,21,37,6.0,3.5,0.005 +2015-10-17 01:11:02:670459,21,38,6.0,4.0,0.005 +2015-10-17 01:11:02:702734,21,39,6.0,4.5,0.004 +2015-10-17 01:11:02:734755,21,40,6.0,5.0,0.004 +2015-10-17 01:11:02:766508,21,41,6.0,5.5,0.004 +2015-10-17 01:11:02:800467,21,42,6.0,6.0,0.005 +2015-10-17 01:11:02:836414,21,43,6.0,6.5,0.005 +2015-10-17 01:11:02:872761,21,44,6.0,7.0,0.006 +2015-10-17 01:11:02:872761,21,44,6.0,7.0,0.006 +2015-10-17 01:11:02:904323,21,45,6.0,7.5,0.007 +2015-10-17 01:11:02:940948,21,46,6.0,8.0,0.007 +2015-10-17 01:11:02:974581,21,47,6.0,8.5,0.008 +2015-10-17 01:11:03:006849,21,48,6.0,9.0,0.008 +2015-10-17 01:11:03:038800,21,49,6.0,9.5,0.009 +2015-10-17 01:11:03:074640,21,50,6.0,10.0,0.009 +2015-10-17 01:11:03:106781,21,51,6.0,10.5,0.009 +2015-10-17 01:11:03:142240,21,52,6.0,11.0,0.008 +2015-10-17 01:11:03:175165,21,53,6.0,11.5,0.008 +2015-10-17 01:11:03:207170,21,54,6.0,12.0,0.007 +2015-10-17 01:11:03:239109,21,55,6.0,12.5,0.007 +2015-10-17 01:11:03:271080,21,56,6.0,13.0,0.006 +2015-10-17 01:11:03:302795,21,57,6.0,13.5,0.005 +2015-10-17 01:11:03:335289,21,58,6.0,14.0,0.005 +2015-10-17 01:11:03:366589,21,59,6.0,14.5,0.004 +2015-10-17 01:11:03:366589,21,59,6.0,14.5,0.004 +2015-10-17 01:11:04:373450,22,0,7.0,-15.0,0.005 +2015-10-17 01:11:04:410187,22,1,7.0,-14.5,0.005 +2015-10-17 01:11:04:443059,22,2,7.0,-14.0,0.006 +2015-10-17 01:11:04:480140,22,3,7.0,-13.5,0.007 +2015-10-17 01:11:04:517370,22,4,7.0,-13.0,0.008 +2015-10-17 01:11:04:551251,22,5,7.0,-12.5,0.009 +2015-10-17 01:11:04:586899,22,6,7.0,-12.0,0.01 +2015-10-17 01:11:04:619115,22,7,7.0,-11.5,0.012 +2015-10-17 01:11:04:652203,22,8,7.0,-11.0,0.013 +2015-10-17 01:11:04:688169,22,9,7.0,-10.5,0.014 +2015-10-17 01:11:04:722691,22,10,7.0,-10.0,0.014 +2015-10-17 01:11:04:757976,22,11,7.0,-9.5,0.014 +2015-10-17 01:11:04:794620,22,12,7.0,-9.0,0.013 +2015-10-17 01:11:04:826313,22,13,7.0,-8.5,0.012 +2015-10-17 01:11:04:857994,22,14,7.0,-8.0,0.01 +2015-10-17 01:11:04:890146,22,15,7.0,-7.5,0.009 +2015-10-17 01:11:04:923836,22,16,7.0,-7.0,0.008 +2015-10-17 01:11:04:957716,22,17,7.0,-6.5,0.007 +2015-10-17 01:11:04:993000,22,18,7.0,-6.0,0.006 +2015-10-17 01:11:04:993000,22,18,7.0,-6.0,0.006 +2015-10-17 01:11:05:026527,22,19,7.0,-5.5,0.005 +2015-10-17 01:11:05:063139,22,20,7.0,-5.0,0.005 +2015-10-17 01:11:05:099270,22,21,7.0,-4.5,0.005 +2015-10-17 01:11:05:136324,22,22,7.0,-4.0,0.006 +2015-10-17 01:11:05:172027,22,23,7.0,-3.5,0.007 +2015-10-17 01:11:05:208024,22,24,7.0,-3.0,0.008 +2015-10-17 01:11:05:239943,22,25,7.0,-2.5,0.009 +2015-10-17 01:11:05:276246,22,26,7.0,-2.0,0.01 +2015-10-17 01:11:05:309822,22,27,7.0,-1.5,0.012 +2015-10-17 01:11:05:341970,22,28,7.0,-1.0,0.013 +2015-10-17 01:11:05:378810,22,29,7.0,-0.5,0.014 +2015-10-17 01:11:05:416029,22,30,7.0,0.0,0.014 +2015-10-17 01:11:05:452491,22,31,7.0,0.5,0.014 +2015-10-17 01:11:05:488970,22,32,7.0,1.0,0.013 +2015-10-17 01:11:05:524967,22,33,7.0,1.5,0.012 +2015-10-17 01:11:05:560729,22,34,7.0,2.0,0.01 +2015-10-17 01:11:05:596834,22,35,7.0,2.5,0.009 +2015-10-17 01:11:05:632880,22,36,7.0,3.0,0.008 +2015-10-17 01:11:05:670069,22,37,7.0,3.5,0.007 +2015-10-17 01:11:05:705991,22,38,7.0,4.0,0.006 +2015-10-17 01:11:05:743225,22,39,7.0,4.5,0.005 +2015-10-17 01:11:05:775013,22,40,7.0,5.0,0.005 +2015-10-17 01:11:05:808052,22,41,7.0,5.5,0.005 +2015-10-17 01:11:05:841958,22,42,7.0,6.0,0.006 +2015-10-17 01:11:05:878423,22,43,7.0,6.5,0.007 +2015-10-17 01:11:05:914346,22,44,7.0,7.0,0.008 +2015-10-17 01:11:05:951587,22,45,7.0,7.5,0.009 +2015-10-17 01:11:05:988045,22,46,7.0,8.0,0.01 +2015-10-17 01:11:06:024501,22,47,7.0,8.5,0.012 +2015-10-17 01:11:06:024501,22,47,7.0,8.5,0.012 +2015-10-17 01:11:06:060427,22,48,7.0,9.0,0.013 +2015-10-17 01:11:06:097645,22,49,7.0,9.5,0.014 +2015-10-17 01:11:06:133656,22,50,7.0,10.0,0.014 +2015-10-17 01:11:06:168817,22,51,7.0,10.5,0.014 +2015-10-17 01:11:06:206152,22,52,7.0,11.0,0.013 +2015-10-17 01:11:06:242196,22,53,7.0,11.5,0.012 +2015-10-17 01:11:06:279419,22,54,7.0,12.0,0.01 +2015-10-17 01:11:06:310919,22,55,7.0,12.5,0.009 +2015-10-17 01:11:06:345726,22,56,7.0,13.0,0.008 +2015-10-17 01:11:06:382078,22,57,7.0,13.5,0.007 +2015-10-17 01:11:06:416343,22,58,7.0,14.0,0.006 +2015-10-17 01:11:06:448443,22,59,7.0,14.5,0.005 +2015-10-17 01:11:06:448443,22,59,7.0,14.5,0.005 +2015-10-17 01:11:07:450822,23,0,8.0,-15.0,0.005 +2015-10-17 01:11:07:483052,23,1,8.0,-14.5,0.006 +2015-10-17 01:11:07:516511,23,2,8.0,-14.0,0.007 +2015-10-17 01:11:07:550621,23,3,8.0,-13.5,0.009 +2015-10-17 01:11:07:582591,23,4,8.0,-13.0,0.01 +2015-10-17 01:11:07:617763,23,5,8.0,-12.5,0.013 +2015-10-17 01:11:07:651335,23,6,8.0,-12.0,0.016 +2015-10-17 01:11:07:684341,23,7,8.0,-11.5,0.019 +2015-10-17 01:11:07:719139,23,8,8.0,-11.0,0.023 +2015-10-17 01:11:07:755208,23,9,8.0,-10.5,0.026 +2015-10-17 01:11:07:789341,23,10,8.0,-10.0,0.027 +2015-10-17 01:11:07:820498,23,11,8.0,-9.5,0.026 +2015-10-17 01:11:07:853101,23,12,8.0,-9.0,0.023 +2015-10-17 01:11:07:884305,23,13,8.0,-8.5,0.019 +2015-10-17 01:11:07:915846,23,14,8.0,-8.0,0.016 +2015-10-17 01:11:07:948381,23,15,8.0,-7.5,0.013 +2015-10-17 01:11:07:981384,23,16,8.0,-7.0,0.01 +2015-10-17 01:11:08:013921,23,17,8.0,-6.5,0.009 +2015-10-17 01:11:08:044873,23,18,8.0,-6.0,0.007 +2015-10-17 01:11:08:076668,23,19,8.0,-5.5,0.006 +2015-10-17 01:11:08:076668,23,19,8.0,-5.5,0.006 +2015-10-17 01:11:08:109787,23,20,8.0,-5.0,0.005 +2015-10-17 01:11:08:141706,23,21,8.0,-4.5,0.006 +2015-10-17 01:11:08:177773,23,22,8.0,-4.0,0.007 +2015-10-17 01:11:08:210005,23,23,8.0,-3.5,0.009 +2015-10-17 01:11:08:242213,23,24,8.0,-3.0,0.01 +2015-10-17 01:11:08:278213,23,25,8.0,-2.5,0.013 +2015-10-17 01:11:08:313322,23,26,8.0,-2.0,0.016 +2015-10-17 01:11:08:347317,23,27,8.0,-1.5,0.019 +2015-10-17 01:11:08:379940,23,28,8.0,-1.0,0.023 +2015-10-17 01:11:08:411649,23,29,8.0,-0.5,0.026 +2015-10-17 01:11:08:442521,23,30,8.0,0.0,0.027 +2015-10-17 01:11:08:476938,23,31,8.0,0.5,0.026 +2015-10-17 01:11:08:508589,23,32,8.0,1.0,0.023 +2015-10-17 01:11:08:545154,23,33,8.0,1.5,0.019 +2015-10-17 01:11:08:577210,23,34,8.0,2.0,0.016 +2015-10-17 01:11:08:609518,23,35,8.0,2.5,0.013 +2015-10-17 01:11:08:642610,23,36,8.0,3.0,0.01 +2015-10-17 01:11:08:673655,23,37,8.0,3.5,0.009 +2015-10-17 01:11:08:706113,23,38,8.0,4.0,0.007 +2015-10-17 01:11:08:738527,23,39,8.0,4.5,0.006 +2015-10-17 01:11:08:774025,23,40,8.0,5.0,0.005 +2015-10-17 01:11:08:807722,23,41,8.0,5.5,0.006 +2015-10-17 01:11:08:842016,23,42,8.0,6.0,0.007 +2015-10-17 01:11:08:874001,23,43,8.0,6.5,0.009 +2015-10-17 01:11:08:906598,23,44,8.0,7.0,0.01 +2015-10-17 01:11:08:940402,23,45,8.0,7.5,0.013 +2015-10-17 01:11:08:972860,23,46,8.0,8.0,0.016 +2015-10-17 01:11:09:005055,23,47,8.0,8.5,0.019 +2015-10-17 01:11:09:035943,23,48,8.0,9.0,0.023 +2015-10-17 01:11:09:069550,23,49,8.0,9.5,0.026 +2015-10-17 01:11:09:101290,23,50,8.0,10.0,0.027 +2015-10-17 01:11:09:101290,23,50,8.0,10.0,0.027 +2015-10-17 01:11:09:136269,23,51,8.0,10.5,0.026 +2015-10-17 01:11:09:171094,23,52,8.0,11.0,0.023 +2015-10-17 01:11:09:202325,23,53,8.0,11.5,0.019 +2015-10-17 01:11:09:234450,23,54,8.0,12.0,0.016 +2015-10-17 01:11:09:267054,23,55,8.0,12.5,0.013 +2015-10-17 01:11:09:298151,23,56,8.0,13.0,0.01 +2015-10-17 01:11:09:330341,23,57,8.0,13.5,0.009 +2015-10-17 01:11:09:366488,23,58,8.0,14.0,0.007 +2015-10-17 01:11:09:399523,23,59,8.0,14.5,0.006 +2015-10-17 01:11:09:399523,23,59,8.0,14.5,0.006 +2015-10-17 01:11:10:401957,24,0,9.0,-15.0,0.006 +2015-10-17 01:11:10:433130,24,1,9.0,-14.5,0.007 +2015-10-17 01:11:10:464488,24,2,9.0,-14.0,0.008 +2015-10-17 01:11:10:499136,24,3,9.0,-13.5,0.01 +2015-10-17 01:11:10:531737,24,4,9.0,-13.0,0.013 +2015-10-17 01:11:10:567428,24,5,9.0,-12.5,0.017 +2015-10-17 01:11:10:604098,24,6,9.0,-12.0,0.023 +2015-10-17 01:11:10:636425,24,7,9.0,-11.5,0.031 +2015-10-17 01:11:10:668344,24,8,9.0,-11.0,0.044 +2015-10-17 01:11:10:700439,24,9,9.0,-10.5,0.057 +2015-10-17 01:11:10:732454,24,10,9.0,-10.0,0.064 +2015-10-17 01:11:10:764765,24,11,9.0,-9.5,0.057 +2015-10-17 01:11:10:799360,24,12,9.0,-9.0,0.044 +2015-10-17 01:11:10:836093,24,13,9.0,-8.5,0.031 +2015-10-17 01:11:10:868555,24,14,9.0,-8.0,0.023 +2015-10-17 01:11:10:900626,24,15,9.0,-7.5,0.017 +2015-10-17 01:11:10:932630,24,16,9.0,-7.0,0.013 +2015-10-17 01:11:10:964654,24,17,9.0,-6.5,0.01 +2015-10-17 01:11:10:996696,24,18,9.0,-6.0,0.008 +2015-10-17 01:11:11:031852,24,19,9.0,-5.5,0.007 +2015-10-17 01:11:11:067862,24,20,9.0,-5.0,0.006 +2015-10-17 01:11:11:100492,24,21,9.0,-4.5,0.007 +2015-10-17 01:11:11:132350,24,22,9.0,-4.0,0.008 +2015-10-17 01:11:11:167263,24,23,9.0,-3.5,0.01 +2015-10-17 01:11:11:200095,24,24,9.0,-3.0,0.013 +2015-10-17 01:11:11:233366,24,25,9.0,-2.5,0.017 +2015-10-17 01:11:11:233366,24,25,9.0,-2.5,0.017 +2015-10-17 01:11:11:267482,24,26,9.0,-2.0,0.023 +2015-10-17 01:11:11:300946,24,27,9.0,-1.5,0.031 +2015-10-17 01:11:11:332928,24,28,9.0,-1.0,0.044 +2015-10-17 01:11:11:365024,24,29,9.0,-0.5,0.057 +2015-10-17 01:11:11:401092,24,30,9.0,0.0,0.064 +2015-10-17 01:11:11:437103,24,31,9.0,0.5,0.057 +2015-10-17 01:11:11:468923,24,32,9.0,1.0,0.044 +2015-10-17 01:11:11:500751,24,33,9.0,1.5,0.031 +2015-10-17 01:11:11:533440,24,34,9.0,2.0,0.023 +2015-10-17 01:11:11:565109,24,35,9.0,2.5,0.017 +2015-10-17 01:11:11:597229,24,36,9.0,3.0,0.013 +2015-10-17 01:11:11:629183,24,37,9.0,3.5,0.01 +2015-10-17 01:11:11:661155,24,38,9.0,4.0,0.008 +2015-10-17 01:11:11:693290,24,39,9.0,4.5,0.007 +2015-10-17 01:11:11:725294,24,40,9.0,5.0,0.006 +2015-10-17 01:11:11:760135,24,41,9.0,5.5,0.007 +2015-10-17 01:11:11:796690,24,42,9.0,6.0,0.008 +2015-10-17 01:11:11:829344,24,43,9.0,6.5,0.01 +2015-10-17 01:11:11:861423,24,44,9.0,7.0,0.013 +2015-10-17 01:11:11:893450,24,45,9.0,7.5,0.017 +2015-10-17 01:11:11:925441,24,46,9.0,8.0,0.023 +2015-10-17 01:11:11:957474,24,47,9.0,8.5,0.031 +2015-10-17 01:11:11:989065,24,48,9.0,9.0,0.044 +2015-10-17 01:11:12:021363,24,49,9.0,9.5,0.057 +2015-10-17 01:11:12:052620,24,50,9.0,10.0,0.064 +2015-10-17 01:11:12:085134,24,51,9.0,10.5,0.057 +2015-10-17 01:11:12:120645,24,52,9.0,11.0,0.044 +2015-10-17 01:11:12:153712,24,53,9.0,11.5,0.031 +2015-10-17 01:11:12:188378,24,54,9.0,12.0,0.023 +2015-10-17 01:11:12:219587,24,55,9.0,12.5,0.017 +2015-10-17 01:11:12:253576,24,56,9.0,13.0,0.013 +2015-10-17 01:11:12:253576,24,56,9.0,13.0,0.013 +2015-10-17 01:11:12:288867,24,57,9.0,13.5,0.01 +2015-10-17 01:11:12:323041,24,58,9.0,14.0,0.008 +2015-10-17 01:11:12:358238,24,59,9.0,14.5,0.007 +2015-10-17 01:11:13:364738,25,0,10.0,-15.0,0.006 +2015-10-17 01:11:13:364738,25,0,10.0,-15.0,0.006 +2015-10-17 01:11:13:398989,25,1,10.0,-14.5,0.007 +2015-10-17 01:11:13:430928,25,2,10.0,-14.0,0.009 +2015-10-17 01:11:13:462987,25,3,10.0,-13.5,0.011 +2015-10-17 01:11:13:495065,25,4,10.0,-13.0,0.014 +2015-10-17 01:11:13:527041,25,5,10.0,-12.5,0.019 +2015-10-17 01:11:13:563205,25,6,10.0,-12.0,0.027 +2015-10-17 01:11:13:599191,25,7,10.0,-11.5,0.04 +2015-10-17 01:11:13:630987,25,8,10.0,-11.0,0.064 +2015-10-17 01:11:13:663578,25,9,10.0,-10.5,0.099 +2015-10-17 01:11:13:695204,25,10,10.0,-10.0,0.117 +2015-10-17 01:11:13:727221,25,11,10.0,-9.5,0.099 +2015-10-17 01:11:13:761234,25,12,10.0,-9.0,0.064 +2015-10-17 01:11:13:797262,25,13,10.0,-8.5,0.04 +2015-10-17 01:11:13:831310,25,14,10.0,-8.0,0.027 +2015-10-17 01:11:13:863401,25,15,10.0,-7.5,0.019 +2015-10-17 01:11:13:895399,25,16,10.0,-7.0,0.014 +2015-10-17 01:11:13:927433,25,17,10.0,-6.5,0.011 +2015-10-17 01:11:13:963115,25,18,10.0,-6.0,0.009 +2015-10-17 01:11:13:995203,25,19,10.0,-5.5,0.007 +2015-10-17 01:11:14:031538,25,20,10.0,-5.0,0.006 +2015-10-17 01:11:14:063030,25,21,10.0,-4.5,0.007 +2015-10-17 01:11:14:095582,25,22,10.0,-4.0,0.009 +2015-10-17 01:11:14:127579,25,23,10.0,-3.5,0.011 +2015-10-17 01:11:14:159673,25,24,10.0,-3.0,0.014 +2015-10-17 01:11:14:191656,25,25,10.0,-2.5,0.019 +2015-10-17 01:11:14:223654,25,26,10.0,-2.0,0.027 +2015-10-17 01:11:14:255733,25,27,10.0,-1.5,0.04 +2015-10-17 01:11:14:290503,25,28,10.0,-1.0,0.064 +2015-10-17 01:11:14:328054,25,29,10.0,-0.5,0.099 +2015-10-17 01:11:14:359885,25,30,10.0,0.0,0.117 +2015-10-17 01:11:14:391916,25,31,10.0,0.5,0.099 +2015-10-17 01:11:14:391916,25,31,10.0,0.5,0.099 +2015-10-17 01:11:14:423912,25,32,10.0,1.0,0.064 +2015-10-17 01:11:14:455976,25,33,10.0,1.5,0.04 +2015-10-17 01:11:14:488204,25,34,10.0,2.0,0.027 +2015-10-17 01:11:14:520096,25,35,10.0,2.5,0.019 +2015-10-17 01:11:14:552119,25,36,10.0,3.0,0.014 +2015-10-17 01:11:14:588325,25,37,10.0,3.5,0.011 +2015-10-17 01:11:14:624133,25,38,10.0,4.0,0.009 +2015-10-17 01:11:14:656179,25,39,10.0,4.5,0.007 +2015-10-17 01:11:14:688693,25,40,10.0,5.0,0.006 +2015-10-17 01:11:14:720309,25,41,10.0,5.5,0.007 +2015-10-17 01:11:14:752264,25,42,10.0,6.0,0.009 +2015-10-17 01:11:14:784665,25,43,10.0,6.5,0.011 +2015-10-17 01:11:14:816318,25,44,10.0,7.0,0.014 +2015-10-17 01:11:14:848373,25,45,10.0,7.5,0.019 +2015-10-17 01:11:14:880453,25,46,10.0,8.0,0.027 +2015-10-17 01:11:14:916282,25,47,10.0,8.5,0.04 +2015-10-17 01:11:14:952434,25,48,10.0,9.0,0.064 +2015-10-17 01:11:14:984482,25,49,10.0,9.5,0.099 +2015-10-17 01:11:15:016612,25,50,10.0,10.0,0.117 +2015-10-17 01:11:15:048520,25,51,10.0,10.5,0.099 +2015-10-17 01:11:15:080596,25,52,10.0,11.0,0.064 +2015-10-17 01:11:15:112586,25,53,10.0,11.5,0.04 +2015-10-17 01:11:15:144567,25,54,10.0,12.0,0.027 +2015-10-17 01:11:15:176599,25,55,10.0,12.5,0.019 +2015-10-17 01:11:15:208683,25,56,10.0,13.0,0.014 +2015-10-17 01:11:15:240729,25,57,10.0,13.5,0.011 +2015-10-17 01:11:15:272640,25,58,10.0,14.0,0.009 +2015-10-17 01:11:15:304910,25,59,10.0,14.5,0.007 +2015-10-17 01:11:15:304910,25,59,10.0,14.5,0.007 +2015-10-17 01:11:16:306720,26,0,11.0,-15.0,0.006 +2015-10-17 01:11:16:338645,26,1,11.0,-14.5,0.007 +2015-10-17 01:11:16:375509,26,2,11.0,-14.0,0.008 +2015-10-17 01:11:16:409519,26,3,11.0,-13.5,0.01 +2015-10-17 01:11:16:442423,26,4,11.0,-13.0,0.013 +2015-10-17 01:11:16:473509,26,5,11.0,-12.5,0.017 +2015-10-17 01:11:16:509834,26,6,11.0,-12.0,0.023 +2015-10-17 01:11:16:541428,26,7,11.0,-11.5,0.031 +2015-10-17 01:11:16:541428,26,7,11.0,-11.5,0.031 +2015-10-17 01:11:16:577064,26,8,11.0,-11.0,0.044 +2015-10-17 01:11:16:611705,26,9,11.0,-10.5,0.057 +2015-10-17 01:11:16:643910,26,10,11.0,-10.0,0.064 +2015-10-17 01:11:16:676088,26,11,11.0,-9.5,0.057 +2015-10-17 01:11:16:707675,26,12,11.0,-9.0,0.044 +2015-10-17 01:11:16:741298,26,13,11.0,-8.5,0.031 +2015-10-17 01:11:16:777904,26,14,11.0,-8.0,0.023 +2015-10-17 01:11:16:811664,26,15,11.0,-7.5,0.017 +2015-10-17 01:11:16:849134,26,16,11.0,-7.0,0.013 +2015-10-17 01:11:16:882232,26,17,11.0,-6.5,0.01 +2015-10-17 01:11:16:914086,26,18,11.0,-6.0,0.008 +2015-10-17 01:11:16:946187,26,19,11.0,-5.5,0.007 +2015-10-17 01:11:16:978260,26,20,11.0,-5.0,0.006 +2015-10-17 01:11:17:009936,26,21,11.0,-4.5,0.007 +2015-10-17 01:11:17:042324,26,22,11.0,-4.0,0.008 +2015-10-17 01:11:17:076318,26,23,11.0,-3.5,0.01 +2015-10-17 01:11:17:107561,26,24,11.0,-3.0,0.013 +2015-10-17 01:11:17:139347,26,25,11.0,-2.5,0.017 +2015-10-17 01:11:17:176038,26,26,11.0,-2.0,0.023 +2015-10-17 01:11:17:209541,26,27,11.0,-1.5,0.031 +2015-10-17 01:11:17:242314,26,28,11.0,-1.0,0.044 +2015-10-17 01:11:17:277103,26,29,11.0,-0.5,0.057 +2015-10-17 01:11:17:311857,26,30,11.0,0.0,0.064 +2015-10-17 01:11:17:346144,26,31,11.0,0.5,0.057 +2015-10-17 01:11:17:382649,26,32,11.0,1.0,0.044 +2015-10-17 01:11:17:416914,26,33,11.0,1.5,0.031 +2015-10-17 01:11:17:448772,26,34,11.0,2.0,0.023 +2015-10-17 01:11:17:482785,26,35,11.0,2.5,0.017 +2015-10-17 01:11:17:514633,26,36,11.0,3.0,0.013 +2015-10-17 01:11:17:546667,26,37,11.0,3.5,0.01 +2015-10-17 01:11:17:546667,26,37,11.0,3.5,0.01 +2015-10-17 01:11:17:578726,26,38,11.0,4.0,0.008 +2015-10-17 01:11:17:610693,26,39,11.0,4.5,0.007 +2015-10-17 01:11:17:644159,26,40,11.0,5.0,0.006 +2015-10-17 01:11:17:681482,26,41,11.0,5.5,0.007 +2015-10-17 01:11:17:714813,26,42,11.0,6.0,0.008 +2015-10-17 01:11:17:746815,26,43,11.0,6.5,0.01 +2015-10-17 01:11:17:778831,26,44,11.0,7.0,0.013 +2015-10-17 01:11:17:810869,26,45,11.0,7.5,0.017 +2015-10-17 01:11:17:846683,26,46,11.0,8.0,0.023 +2015-10-17 01:11:17:878272,26,47,11.0,8.5,0.031 +2015-10-17 01:11:17:913829,26,48,11.0,9.0,0.044 +2015-10-17 01:11:17:950980,26,49,11.0,9.5,0.057 +2015-10-17 01:11:17:986670,26,50,11.0,10.0,0.064 +2015-10-17 01:11:18:019567,26,51,11.0,10.5,0.057 +2015-10-17 01:11:18:055591,26,52,11.0,11.0,0.044 +2015-10-17 01:11:18:090850,26,53,11.0,11.5,0.031 +2015-10-17 01:11:18:123044,26,54,11.0,12.0,0.023 +2015-10-17 01:11:18:155035,26,55,11.0,12.5,0.017 +2015-10-17 01:11:18:191517,26,56,11.0,13.0,0.013 +2015-10-17 01:11:18:227188,26,57,11.0,13.5,0.01 +2015-10-17 01:11:18:259208,26,58,11.0,14.0,0.008 +2015-10-17 01:11:18:291191,26,59,11.0,14.5,0.007 +2015-10-17 01:11:18:291191,26,59,11.0,14.5,0.007 +2015-10-17 01:11:19:296219,27,0,12.0,-15.0,0.005 +2015-10-17 01:11:19:331471,27,1,12.0,-14.5,0.006 +2015-10-17 01:11:19:368155,27,2,12.0,-14.0,0.007 +2015-10-17 01:11:19:400201,27,3,12.0,-13.5,0.009 +2015-10-17 01:11:19:432264,27,4,12.0,-13.0,0.01 +2015-10-17 01:11:19:464272,27,5,12.0,-12.5,0.013 +2015-10-17 01:11:19:496341,27,6,12.0,-12.0,0.016 +2015-10-17 01:11:19:527901,27,7,12.0,-11.5,0.019 +2015-10-17 01:11:19:560306,27,8,12.0,-11.0,0.023 +2015-10-17 01:11:19:592369,27,9,12.0,-10.5,0.026 +2015-10-17 01:11:19:623347,27,10,12.0,-10.0,0.027 +2015-10-17 01:11:19:656600,27,11,12.0,-9.5,0.026 +2015-10-17 01:11:19:690937,27,12,12.0,-9.0,0.023 +2015-10-17 01:11:19:724308,27,13,12.0,-8.5,0.019 +2015-10-17 01:11:19:724308,27,13,12.0,-8.5,0.019 +2015-10-17 01:11:19:755686,27,14,12.0,-8.0,0.016 +2015-10-17 01:11:19:790627,27,15,12.0,-7.5,0.013 +2015-10-17 01:11:19:826433,27,16,12.0,-7.0,0.01 +2015-10-17 01:11:19:859337,27,17,12.0,-6.5,0.009 +2015-10-17 01:11:19:892033,27,18,12.0,-6.0,0.007 +2015-10-17 01:11:19:928411,27,19,12.0,-5.5,0.006 +2015-10-17 01:11:19:961076,27,20,12.0,-5.0,0.005 +2015-10-17 01:11:19:992444,27,21,12.0,-4.5,0.006 +2015-10-17 01:11:20:024223,27,22,12.0,-4.0,0.007 +2015-10-17 01:11:20:056533,27,23,12.0,-3.5,0.009 +2015-10-17 01:11:20:089417,27,24,12.0,-3.0,0.01 +2015-10-17 01:11:20:123831,27,25,12.0,-2.5,0.013 +2015-10-17 01:11:20:155752,27,26,12.0,-2.0,0.016 +2015-10-17 01:11:20:189244,27,27,12.0,-1.5,0.019 +2015-10-17 01:11:20:224699,27,28,12.0,-1.0,0.023 +2015-10-17 01:11:20:257719,27,29,12.0,-0.5,0.026 +2015-10-17 01:11:20:291543,27,30,12.0,0.0,0.027 +2015-10-17 01:11:20:325222,27,31,12.0,0.5,0.026 +2015-10-17 01:11:20:358175,27,32,12.0,1.0,0.023 +2015-10-17 01:11:20:393714,27,33,12.0,1.5,0.019 +2015-10-17 01:11:20:425858,27,34,12.0,2.0,0.016 +2015-10-17 01:11:20:457772,27,35,12.0,2.5,0.013 +2015-10-17 01:11:20:493706,27,36,12.0,3.0,0.01 +2015-10-17 01:11:20:529417,27,37,12.0,3.5,0.009 +2015-10-17 01:11:20:561326,27,38,12.0,4.0,0.007 +2015-10-17 01:11:20:593404,27,39,12.0,4.5,0.006 +2015-10-17 01:11:20:625401,27,40,12.0,5.0,0.005 +2015-10-17 01:11:20:657392,27,41,12.0,5.5,0.006 +2015-10-17 01:11:20:689485,27,42,12.0,6.0,0.007 +2015-10-17 01:11:20:724462,27,43,12.0,6.5,0.009 +2015-10-17 01:11:20:724462,27,43,12.0,6.5,0.009 +2015-10-17 01:11:20:760526,27,44,12.0,7.0,0.01 +2015-10-17 01:11:20:794126,27,45,12.0,7.5,0.013 +2015-10-17 01:11:20:826735,27,46,12.0,8.0,0.016 +2015-10-17 01:11:20:859909,27,47,12.0,8.5,0.019 +2015-10-17 01:11:20:893898,27,48,12.0,9.0,0.023 +2015-10-17 01:11:20:927587,27,49,12.0,9.5,0.026 +2015-10-17 01:11:20:960936,27,50,12.0,10.0,0.027 +2015-10-17 01:11:20:993307,27,51,12.0,10.5,0.026 +2015-10-17 01:11:21:025764,27,52,12.0,11.0,0.023 +2015-10-17 01:11:21:057775,27,53,12.0,11.5,0.019 +2015-10-17 01:11:21:089809,27,54,12.0,12.0,0.016 +2015-10-17 01:11:21:125447,27,55,12.0,12.5,0.013 +2015-10-17 01:11:21:161643,27,56,12.0,13.0,0.01 +2015-10-17 01:11:21:194063,27,57,12.0,13.5,0.009 +2015-10-17 01:11:21:226088,27,58,12.0,14.0,0.007 +2015-10-17 01:11:21:262295,27,59,12.0,14.5,0.006 +2015-10-17 01:11:21:262295,27,59,12.0,14.5,0.006 +2015-10-17 01:11:22:267141,28,0,13.0,-15.0,0.005 +2015-10-17 01:11:22:299025,28,1,13.0,-14.5,0.005 +2015-10-17 01:11:22:331048,28,2,13.0,-14.0,0.006 +2015-10-17 01:11:22:362817,28,3,13.0,-13.5,0.007 +2015-10-17 01:11:22:394941,28,4,13.0,-13.0,0.008 +2015-10-17 01:11:22:430130,28,5,13.0,-12.5,0.009 +2015-10-17 01:11:22:464505,28,6,13.0,-12.0,0.01 +2015-10-17 01:11:22:500142,28,7,13.0,-11.5,0.012 +2015-10-17 01:11:22:535084,28,8,13.0,-11.0,0.013 +2015-10-17 01:11:22:570487,28,9,13.0,-10.5,0.014 +2015-10-17 01:11:22:606909,28,10,13.0,-10.0,0.014 +2015-10-17 01:11:22:643620,28,11,13.0,-9.5,0.014 +2015-10-17 01:11:22:676121,28,12,13.0,-9.0,0.013 +2015-10-17 01:11:22:709047,28,13,13.0,-8.5,0.012 +2015-10-17 01:11:22:744462,28,14,13.0,-8.0,0.01 +2015-10-17 01:11:22:779412,28,15,13.0,-7.5,0.009 +2015-10-17 01:11:22:813935,28,16,13.0,-7.0,0.008 +2015-10-17 01:11:22:848675,28,17,13.0,-6.5,0.007 +2015-10-17 01:11:22:884143,28,18,13.0,-6.0,0.006 +2015-10-17 01:11:22:884143,28,18,13.0,-6.0,0.006 +2015-10-17 01:11:22:915955,28,19,13.0,-5.5,0.005 +2015-10-17 01:11:22:947602,28,20,13.0,-5.0,0.005 +2015-10-17 01:11:22:979650,28,21,13.0,-4.5,0.005 +2015-10-17 01:11:23:011684,28,22,13.0,-4.0,0.006 +2015-10-17 01:11:23:043447,28,23,13.0,-3.5,0.007 +2015-10-17 01:11:23:075927,28,24,13.0,-3.0,0.008 +2015-10-17 01:11:23:107733,28,25,13.0,-2.5,0.009 +2015-10-17 01:11:23:139780,28,26,13.0,-2.0,0.01 +2015-10-17 01:11:23:171961,28,27,13.0,-1.5,0.012 +2015-10-17 01:11:23:203881,28,28,13.0,-1.0,0.013 +2015-10-17 01:11:23:235896,28,29,13.0,-0.5,0.014 +2015-10-17 01:11:23:271076,28,30,13.0,0.0,0.014 +2015-10-17 01:11:23:304299,28,31,13.0,0.5,0.014 +2015-10-17 01:11:23:338227,28,32,13.0,1.0,0.013 +2015-10-17 01:11:23:371670,28,33,13.0,1.5,0.012 +2015-10-17 01:11:23:404300,28,34,13.0,2.0,0.01 +2015-10-17 01:11:23:438154,28,35,13.0,2.5,0.009 +2015-10-17 01:11:23:472244,28,36,13.0,3.0,0.008 +2015-10-17 01:11:23:506352,28,37,13.0,3.5,0.007 +2015-10-17 01:11:23:538511,28,38,13.0,4.0,0.006 +2015-10-17 01:11:23:572326,28,39,13.0,4.5,0.005 +2015-10-17 01:11:23:603976,28,40,13.0,5.0,0.005 +2015-10-17 01:11:23:636354,28,41,13.0,5.5,0.005 +2015-10-17 01:11:23:669317,28,42,13.0,6.0,0.006 +2015-10-17 01:11:23:700570,28,43,13.0,6.5,0.007 +2015-10-17 01:11:23:732342,28,44,13.0,7.0,0.008 +2015-10-17 01:11:23:764533,28,45,13.0,7.5,0.009 +2015-10-17 01:11:23:801245,28,46,13.0,8.0,0.01 +2015-10-17 01:11:23:836411,28,47,13.0,8.5,0.012 +2015-10-17 01:11:23:869148,28,48,13.0,9.0,0.013 +2015-10-17 01:11:23:904431,28,49,13.0,9.5,0.014 +2015-10-17 01:11:23:904431,28,49,13.0,9.5,0.014 +2015-10-17 01:11:23:936472,28,50,13.0,10.0,0.014 +2015-10-17 01:11:23:971426,28,51,13.0,10.5,0.014 +2015-10-17 01:11:24:003753,28,52,13.0,11.0,0.013 +2015-10-17 01:11:24:037185,28,53,13.0,11.5,0.012 +2015-10-17 01:11:24:068778,28,54,13.0,12.0,0.01 +2015-10-17 01:11:24:102571,28,55,13.0,12.5,0.009 +2015-10-17 01:11:24:134854,28,56,13.0,13.0,0.008 +2015-10-17 01:11:24:168921,28,57,13.0,13.5,0.007 +2015-10-17 01:11:24:201311,28,58,13.0,14.0,0.006 +2015-10-17 01:11:24:233982,28,59,13.0,14.5,0.005 +2015-10-17 01:11:24:233982,28,59,13.0,14.5,0.005 +2015-10-17 01:11:25:234970,29,0,14.0,-15.0,0.004 +2015-10-17 01:11:25:268517,29,1,14.0,-14.5,0.004 +2015-10-17 01:11:25:299686,29,2,14.0,-14.0,0.005 +2015-10-17 01:11:25:332607,29,3,14.0,-13.5,0.005 +2015-10-17 01:11:25:366007,29,4,14.0,-13.0,0.006 +2015-10-17 01:11:25:398206,29,5,14.0,-12.5,0.007 +2015-10-17 01:11:25:430583,29,6,14.0,-12.0,0.007 +2015-10-17 01:11:25:464360,29,7,14.0,-11.5,0.008 +2015-10-17 01:11:25:499716,29,8,14.0,-11.0,0.008 +2015-10-17 01:11:25:534214,29,9,14.0,-10.5,0.009 +2015-10-17 01:11:25:566148,29,10,14.0,-10.0,0.009 +2015-10-17 01:11:25:600512,29,11,14.0,-9.5,0.009 +2015-10-17 01:11:25:634577,29,12,14.0,-9.0,0.008 +2015-10-17 01:11:25:670269,29,13,14.0,-8.5,0.008 +2015-10-17 01:11:25:701848,29,14,14.0,-8.0,0.007 +2015-10-17 01:11:25:733103,29,15,14.0,-7.5,0.007 +2015-10-17 01:11:25:764399,29,16,14.0,-7.0,0.006 +2015-10-17 01:11:25:798346,29,17,14.0,-6.5,0.005 +2015-10-17 01:11:25:830162,29,18,14.0,-6.0,0.005 +2015-10-17 01:11:25:862003,29,19,14.0,-5.5,0.004 +2015-10-17 01:11:25:894613,29,20,14.0,-5.0,0.004 +2015-10-17 01:11:25:926397,29,21,14.0,-4.5,0.004 +2015-10-17 01:11:25:957932,29,22,14.0,-4.0,0.005 +2015-10-17 01:11:25:990123,29,23,14.0,-3.5,0.005 +2015-10-17 01:11:26:023755,29,24,14.0,-3.0,0.006 +2015-10-17 01:11:26:056633,29,25,14.0,-2.5,0.007 +2015-10-17 01:11:26:056633,29,25,14.0,-2.5,0.007 +2015-10-17 01:11:26:090893,29,26,14.0,-2.0,0.007 +2015-10-17 01:11:26:123230,29,27,14.0,-1.5,0.008 +2015-10-17 01:11:26:158665,29,28,14.0,-1.0,0.008 +2015-10-17 01:11:26:190681,29,29,14.0,-0.5,0.009 +2015-10-17 01:11:26:222644,29,30,14.0,0.0,0.009 +2015-10-17 01:11:26:254944,29,31,14.0,0.5,0.009 +2015-10-17 01:11:26:291364,29,32,14.0,1.0,0.008 +2015-10-17 01:11:26:326766,29,33,14.0,1.5,0.008 +2015-10-17 01:11:26:358788,29,34,14.0,2.0,0.007 +2015-10-17 01:11:26:390858,29,35,14.0,2.5,0.007 +2015-10-17 01:11:26:422962,29,36,14.0,3.0,0.006 +2015-10-17 01:11:26:454909,29,37,14.0,3.5,0.005 +2015-10-17 01:11:26:486918,29,38,14.0,4.0,0.005 +2015-10-17 01:11:26:518661,29,39,14.0,4.5,0.004 +2015-10-17 01:11:26:551004,29,40,14.0,5.0,0.004 +2015-10-17 01:11:26:586368,29,41,14.0,5.5,0.004 +2015-10-17 01:11:26:617873,29,42,14.0,6.0,0.005 +2015-10-17 01:11:26:650834,29,43,14.0,6.5,0.005 +2015-10-17 01:11:26:684210,29,44,14.0,7.0,0.006 +2015-10-17 01:11:26:719232,29,45,14.0,7.5,0.007 +2015-10-17 01:11:26:753185,29,46,14.0,8.0,0.007 +2015-10-17 01:11:26:787679,29,47,14.0,8.5,0.008 +2015-10-17 01:11:26:820007,29,48,14.0,9.0,0.008 +2015-10-17 01:11:26:856872,29,49,14.0,9.5,0.009 +2015-10-17 01:11:26:893332,29,50,14.0,10.0,0.009 +2015-10-17 01:11:26:929321,29,51,14.0,10.5,0.009 +2015-10-17 01:11:26:963438,29,52,14.0,11.0,0.008 +2015-10-17 01:11:26:995258,29,53,14.0,11.5,0.008 +2015-10-17 01:11:27:027436,29,54,14.0,12.0,0.007 +2015-10-17 01:11:27:059787,29,55,14.0,12.5,0.007 +2015-10-17 01:11:27:059787,29,55,14.0,12.5,0.007 +2015-10-17 01:11:27:092425,29,56,14.0,13.0,0.006 +2015-10-17 01:11:27:123336,29,57,14.0,13.5,0.005 +2015-10-17 01:11:27:155935,29,58,14.0,14.0,0.005 +2015-10-17 01:11:27:189765,29,59,14.0,14.5,0.004 diff --git a/testsweep.csv b/testsweep.csv new file mode 100644 index 000000000000..0a701bb1955a --- /dev/null +++ b/testsweep.csv @@ -0,0 +1,480 @@ +ts,i_chan0(400),chan0,amplitude +2015-10-17 01:08:25:066818,0,-20.0,0.117 +2015-10-17 01:08:25:272614,1,-19.9,0.117 +2015-10-17 01:08:25:477351,2,-19.8,0.115 +2015-10-17 01:08:25:683882,3,-19.7,0.111 +2015-10-17 01:08:25:890434,4,-19.6,0.106 +2015-10-17 01:08:25:890434,4,-19.6,0.106 +2015-10-17 01:08:26:096571,5,-19.5,0.099 +2015-10-17 01:08:26:302896,6,-19.4,0.092 +2015-10-17 01:08:26:509334,7,-19.3,0.085 +2015-10-17 01:08:26:712697,8,-19.2,0.077 +2015-10-17 01:08:26:914103,9,-19.1,0.071 +2015-10-17 01:08:26:914103,9,-19.1,0.071 +2015-10-17 01:08:27:117491,10,-19.0,0.064 +2015-10-17 01:08:27:323689,11,-18.9,0.058 +2015-10-17 01:08:27:528680,12,-18.8,0.053 +2015-10-17 01:08:27:734919,13,-18.7,0.048 +2015-10-17 01:08:27:941441,14,-18.6,0.044 +2015-10-17 01:08:27:941441,14,-18.6,0.044 +2015-10-17 01:08:28:147449,15,-18.5,0.04 +2015-10-17 01:08:28:354581,16,-18.4,0.037 +2015-10-17 01:08:28:560971,17,-18.3,0.034 +2015-10-17 01:08:28:767400,18,-18.2,0.031 +2015-10-17 01:08:28:973360,19,-18.1,0.029 +2015-10-17 01:08:28:973360,19,-18.1,0.029 +2015-10-17 01:08:29:179899,20,-18.0,0.027 +2015-10-17 01:08:29:386045,21,-17.9,0.025 +2015-10-17 01:08:29:589390,22,-17.8,0.023 +2015-10-17 01:08:29:791675,23,-17.7,0.022 +2015-10-17 01:08:29:995402,24,-17.6,0.02 +2015-10-17 01:08:29:995402,24,-17.6,0.02 +2015-10-17 01:08:30:197972,25,-17.5,0.019 +2015-10-17 01:08:30:399327,26,-17.4,0.018 +2015-10-17 01:08:30:602835,27,-17.3,0.017 +2015-10-17 01:08:30:808404,28,-17.2,0.016 +2015-10-17 01:08:31:012270,29,-17.1,0.015 +2015-10-17 01:08:31:012270,29,-17.1,0.015 +2015-10-17 01:08:31:218347,30,-17.0,0.014 +2015-10-17 01:08:31:425131,31,-16.9,0.013 +2015-10-17 01:08:31:631597,32,-16.8,0.013 +2015-10-17 01:08:31:838021,33,-16.7,0.012 +2015-10-17 01:08:32:044581,34,-16.6,0.011 +2015-10-17 01:08:32:044581,34,-16.6,0.011 +2015-10-17 01:08:32:250560,35,-16.5,0.011 +2015-10-17 01:08:32:457495,36,-16.4,0.01 +2015-10-17 01:08:32:663925,37,-16.3,0.01 +2015-10-17 01:08:32:870353,38,-16.2,0.01 +2015-10-17 01:08:33:076791,39,-16.1,0.009 +2015-10-17 01:08:33:076791,39,-16.1,0.009 +2015-10-17 01:08:33:280467,40,-16.0,0.009 +2015-10-17 01:08:33:486903,41,-15.9,0.008 +2015-10-17 01:08:33:691401,42,-15.8,0.008 +2015-10-17 01:08:33:897834,43,-15.7,0.008 +2015-10-17 01:08:34:104460,44,-15.6,0.007 +2015-10-17 01:08:34:104460,44,-15.6,0.007 +2015-10-17 01:08:34:310444,45,-15.5,0.007 +2015-10-17 01:08:34:517390,46,-15.4,0.007 +2015-10-17 01:08:34:723954,47,-15.3,0.007 +2015-10-17 01:08:34:930402,48,-15.2,0.006 +2015-10-17 01:08:35:136816,49,-15.1,0.006 +2015-10-17 01:08:35:136816,49,-15.1,0.006 +2015-10-17 01:08:35:343415,50,-15.0,0.006 +2015-10-17 01:08:35:549375,51,-14.9,0.006 +2015-10-17 01:08:35:756330,52,-14.8,0.006 +2015-10-17 01:08:35:962227,53,-14.7,0.007 +2015-10-17 01:08:36:169303,54,-14.6,0.007 +2015-10-17 01:08:36:169303,54,-14.6,0.007 +2015-10-17 01:08:36:375811,55,-14.5,0.007 +2015-10-17 01:08:36:581095,56,-14.4,0.007 +2015-10-17 01:08:36:786794,57,-14.3,0.008 +2015-10-17 01:08:36:987164,58,-14.2,0.008 +2015-10-17 01:08:37:189052,59,-14.1,0.008 +2015-10-17 01:08:37:189052,59,-14.1,0.008 +2015-10-17 01:08:37:390893,60,-14.0,0.009 +2015-10-17 01:08:37:598052,61,-13.9,0.009 +2015-10-17 01:08:37:804445,62,-13.8,0.01 +2015-10-17 01:08:38:010402,63,-13.7,0.01 +2015-10-17 01:08:38:217518,64,-13.6,0.01 +2015-10-17 01:08:38:217518,64,-13.6,0.01 +2015-10-17 01:08:38:421855,65,-13.5,0.011 +2015-10-17 01:08:38:623521,66,-13.4,0.011 +2015-10-17 01:08:38:827320,67,-13.3,0.012 +2015-10-17 01:08:39:030795,68,-13.2,0.013 +2015-10-17 01:08:39:233954,69,-13.1,0.013 +2015-10-17 01:08:39:233954,69,-13.1,0.013 +2015-10-17 01:08:39:438425,70,-13.0,0.014 +2015-10-17 01:08:39:641976,71,-12.9,0.015 +2015-10-17 01:08:39:846889,72,-12.8,0.016 +2015-10-17 01:08:40:047851,73,-12.7,0.017 +2015-10-17 01:08:40:250781,74,-12.6,0.018 +2015-10-17 01:08:40:250781,74,-12.6,0.018 +2015-10-17 01:08:40:456381,75,-12.5,0.019 +2015-10-17 01:08:40:663041,76,-12.4,0.02 +2015-10-17 01:08:40:869473,77,-12.3,0.022 +2015-10-17 01:08:41:072945,78,-12.2,0.023 +2015-10-17 01:08:41:275260,79,-12.1,0.025 +2015-10-17 01:08:41:275260,79,-12.1,0.025 +2015-10-17 01:08:41:476995,80,-12.0,0.027 +2015-10-17 01:08:41:678407,81,-11.9,0.029 +2015-10-17 01:08:41:882884,82,-11.8,0.031 +2015-10-17 01:08:42:087207,83,-11.7,0.034 +2015-10-17 01:08:42:291228,84,-11.6,0.037 +2015-10-17 01:08:42:291228,84,-11.6,0.037 +2015-10-17 01:08:42:497550,85,-11.5,0.04 +2015-10-17 01:08:42:700372,86,-11.4,0.044 +2015-10-17 01:08:42:904526,87,-11.3,0.048 +2015-10-17 01:08:43:106188,88,-11.2,0.053 +2015-10-17 01:08:43:312608,89,-11.1,0.058 +2015-10-17 01:08:43:312608,89,-11.1,0.058 +2015-10-17 01:08:43:516957,90,-11.0,0.064 +2015-10-17 01:08:43:722358,91,-10.9,0.071 +2015-10-17 01:08:43:925930,92,-10.8,0.077 +2015-10-17 01:08:44:129729,93,-10.7,0.085 +2015-10-17 01:08:44:333129,94,-10.6,0.092 +2015-10-17 01:08:44:333129,94,-10.6,0.092 +2015-10-17 01:08:44:539686,95,-10.5,0.099 +2015-10-17 01:08:44:746177,96,-10.4,0.106 +2015-10-17 01:08:44:952608,97,-10.3,0.111 +2015-10-17 01:08:45:159087,98,-10.2,0.115 +2015-10-17 01:08:45:365511,99,-10.1,0.117 +2015-10-17 01:08:45:365511,99,-10.1,0.117 +2015-10-17 01:08:45:572074,100,-10.0,0.117 +2015-10-17 01:08:45:778078,101,-9.9,0.117 +2015-10-17 01:08:45:984385,102,-9.8,0.115 +2015-10-17 01:08:46:186357,103,-9.7,0.111 +2015-10-17 01:08:46:391071,104,-9.6,0.106 +2015-10-17 01:08:46:391071,104,-9.6,0.106 +2015-10-17 01:08:46:597429,105,-9.5,0.099 +2015-10-17 01:08:46:801541,106,-9.4,0.092 +2015-10-17 01:08:47:004053,107,-9.3,0.085 +2015-10-17 01:08:47:210587,108,-9.2,0.077 +2015-10-17 01:08:47:416357,109,-9.1,0.071 +2015-10-17 01:08:47:416357,109,-9.1,0.071 +2015-10-17 01:08:47:618598,110,-9.0,0.064 +2015-10-17 01:08:47:820846,111,-8.9,0.058 +2015-10-17 01:08:48:023591,112,-8.8,0.053 +2015-10-17 01:08:48:226031,113,-8.7,0.048 +2015-10-17 01:08:48:432471,114,-8.6,0.044 +2015-10-17 01:08:48:432471,114,-8.6,0.044 +2015-10-17 01:08:48:637543,115,-8.5,0.04 +2015-10-17 01:08:48:841939,116,-8.4,0.037 +2015-10-17 01:08:49:044442,117,-8.3,0.034 +2015-10-17 01:08:49:248390,118,-8.2,0.031 +2015-10-17 01:08:49:452331,119,-8.1,0.029 +2015-10-17 01:08:49:452331,119,-8.1,0.029 +2015-10-17 01:08:49:654798,120,-8.0,0.027 +2015-10-17 01:08:49:855576,121,-7.9,0.025 +2015-10-17 01:08:50:057071,122,-7.8,0.023 +2015-10-17 01:08:50:264153,123,-7.7,0.022 +2015-10-17 01:08:50:468308,124,-7.6,0.02 +2015-10-17 01:08:50:468308,124,-7.6,0.02 +2015-10-17 01:08:50:672899,125,-7.5,0.019 +2015-10-17 01:08:50:876487,126,-7.4,0.018 +2015-10-17 01:08:51:078548,127,-7.3,0.017 +2015-10-17 01:08:51:280437,128,-7.2,0.016 +2015-10-17 01:08:51:487586,129,-7.1,0.015 +2015-10-17 01:08:51:487586,129,-7.1,0.015 +2015-10-17 01:08:51:691382,130,-7.0,0.014 +2015-10-17 01:08:51:896049,131,-6.9,0.013 +2015-10-17 01:08:52:097634,132,-6.8,0.013 +2015-10-17 01:08:52:304223,133,-6.7,0.012 +2015-10-17 01:08:52:511697,134,-6.6,0.011 +2015-10-17 01:08:52:712528,135,-6.5,0.011 +2015-10-17 01:08:52:712528,135,-6.5,0.011 +2015-10-17 01:08:52:919926,136,-6.4,0.01 +2015-10-17 01:08:53:123019,137,-6.3,0.01 +2015-10-17 01:08:53:324753,138,-6.2,0.01 +2015-10-17 01:08:53:528206,139,-6.1,0.009 +2015-10-17 01:08:53:528206,139,-6.1,0.009 +2015-10-17 01:08:53:730047,140,-6.0,0.009 +2015-10-17 01:08:53:934696,141,-5.9,0.008 +2015-10-17 01:08:54:140749,142,-5.8,0.008 +2015-10-17 01:08:54:346396,143,-5.7,0.008 +2015-10-17 01:08:54:547262,144,-5.6,0.007 +2015-10-17 01:08:54:547262,144,-5.6,0.007 +2015-10-17 01:08:54:754296,145,-5.5,0.007 +2015-10-17 01:08:54:958062,146,-5.4,0.007 +2015-10-17 01:08:55:159589,147,-5.3,0.007 +2015-10-17 01:08:55:362231,148,-5.2,0.006 +2015-10-17 01:08:55:567305,149,-5.1,0.006 +2015-10-17 01:08:55:567305,149,-5.1,0.006 +2015-10-17 01:08:55:772267,150,-5.0,0.006 +2015-10-17 01:08:55:973639,151,-4.9,0.006 +2015-10-17 01:08:56:178694,152,-4.8,0.006 +2015-10-17 01:08:56:382744,153,-4.7,0.007 +2015-10-17 01:08:56:585253,154,-4.6,0.007 +2015-10-17 01:08:56:585253,154,-4.6,0.007 +2015-10-17 01:08:56:791481,155,-4.5,0.007 +2015-10-17 01:08:56:994375,156,-4.4,0.007 +2015-10-17 01:08:57:196595,157,-4.3,0.008 +2015-10-17 01:08:57:398339,158,-4.2,0.008 +2015-10-17 01:08:57:602207,159,-4.1,0.008 +2015-10-17 01:08:57:602207,159,-4.1,0.008 +2015-10-17 01:08:57:804439,160,-4.0,0.009 +2015-10-17 01:08:58:009306,161,-3.9,0.009 +2015-10-17 01:08:58:211158,162,-3.8,0.01 +2015-10-17 01:08:58:412695,163,-3.7,0.01 +2015-10-17 01:08:58:618581,164,-3.6,0.01 +2015-10-17 01:08:58:618581,164,-3.6,0.01 +2015-10-17 01:08:58:821444,165,-3.5,0.011 +2015-10-17 01:08:59:022761,166,-3.4,0.011 +2015-10-17 01:08:59:224515,167,-3.3,0.012 +2015-10-17 01:08:59:426049,168,-3.2,0.013 +2015-10-17 01:08:59:631730,169,-3.1,0.013 +2015-10-17 01:08:59:631730,169,-3.1,0.013 +2015-10-17 01:08:59:834439,170,-3.0,0.014 +2015-10-17 01:09:00:039694,171,-2.9,0.015 +2015-10-17 01:09:00:244563,172,-2.8,0.016 +2015-10-17 01:09:00:446875,173,-2.7,0.017 +2015-10-17 01:09:00:653546,174,-2.6,0.018 +2015-10-17 01:09:00:653546,174,-2.6,0.018 +2015-10-17 01:09:00:856120,175,-2.5,0.019 +2015-10-17 01:09:01:057571,176,-2.4,0.02 +2015-10-17 01:09:01:262276,177,-2.3,0.022 +2015-10-17 01:09:01:465554,178,-2.2,0.023 +2015-10-17 01:09:01:670305,179,-2.1,0.025 +2015-10-17 01:09:01:670305,179,-2.1,0.025 +2015-10-17 01:09:01:873423,180,-2.0,0.027 +2015-10-17 01:09:02:080410,181,-1.9,0.029 +2015-10-17 01:09:02:284684,182,-1.8,0.031 +2015-10-17 01:09:02:486174,183,-1.7,0.034 +2015-10-17 01:09:02:688014,184,-1.6,0.037 +2015-10-17 01:09:02:688014,184,-1.6,0.037 +2015-10-17 01:09:02:895173,185,-1.5,0.04 +2015-10-17 01:09:03:099103,186,-1.4,0.044 +2015-10-17 01:09:03:305836,187,-1.3,0.048 +2015-10-17 01:09:03:508621,188,-1.2,0.053 +2015-10-17 01:09:03:709373,189,-1.1,0.058 +2015-10-17 01:09:03:709373,189,-1.1,0.058 +2015-10-17 01:09:03:911310,190,-1.0,0.064 +2015-10-17 01:09:04:117754,191,-0.9,0.071 +2015-10-17 01:09:04:320308,192,-0.8,0.077 +2015-10-17 01:09:04:525469,193,-0.7,0.085 +2015-10-17 01:09:04:730840,194,-0.6,0.092 +2015-10-17 01:09:04:730840,194,-0.6,0.092 +2015-10-17 01:09:04:937340,195,-0.5,0.099 +2015-10-17 01:09:05:140306,196,-0.4,0.106 +2015-10-17 01:09:05:342407,197,-0.3,0.111 +2015-10-17 01:09:05:548100,198,-0.2,0.115 +2015-10-17 01:09:05:753362,199,-0.1,0.117 +2015-10-17 01:09:05:753362,199,-0.1,0.117 +2015-10-17 01:09:05:955718,200,0.0,0.117 +2015-10-17 01:09:06:161382,201,0.1,0.117 +2015-10-17 01:09:06:362839,202,0.2,0.115 +2015-10-17 01:09:06:564141,203,0.3,0.111 +2015-10-17 01:09:06:768455,204,0.4,0.106 +2015-10-17 01:09:06:768455,204,0.4,0.106 +2015-10-17 01:09:06:972218,205,0.5,0.099 +2015-10-17 01:09:07:175029,206,0.6,0.092 +2015-10-17 01:09:07:380009,207,0.7,0.085 +2015-10-17 01:09:07:586292,208,0.8,0.077 +2015-10-17 01:09:07:789344,209,0.9,0.071 +2015-10-17 01:09:07:789344,209,0.9,0.071 +2015-10-17 01:09:07:993746,210,1.0,0.064 +2015-10-17 01:09:08:196331,211,1.1,0.058 +2015-10-17 01:09:08:397974,212,1.2,0.053 +2015-10-17 01:09:08:600244,213,1.3,0.048 +2015-10-17 01:09:08:804068,214,1.4,0.044 +2015-10-17 01:09:08:804068,214,1.4,0.044 +2015-10-17 01:09:09:006069,215,1.5,0.04 +2015-10-17 01:09:09:208774,216,1.6,0.037 +2015-10-17 01:09:09:411084,217,1.7,0.034 +2015-10-17 01:09:09:613102,218,1.8,0.031 +2015-10-17 01:09:09:815551,219,1.9,0.029 +2015-10-17 01:09:09:815551,219,1.9,0.029 +2015-10-17 01:09:10:018059,220,2.0,0.027 +2015-10-17 01:09:10:221442,221,2.1,0.025 +2015-10-17 01:09:10:425992,222,2.2,0.023 +2015-10-17 01:09:10:629908,223,2.3,0.022 +2015-10-17 01:09:10:833956,224,2.4,0.02 +2015-10-17 01:09:10:833956,224,2.4,0.02 +2015-10-17 01:09:11:035923,225,2.5,0.019 +2015-10-17 01:09:11:242522,226,2.6,0.018 +2015-10-17 01:09:11:448470,227,2.7,0.017 +2015-10-17 01:09:11:651542,228,2.8,0.016 +2015-10-17 01:09:11:858015,229,2.9,0.015 +2015-10-17 01:09:11:858015,229,2.9,0.015 +2015-10-17 01:09:12:059396,230,3.0,0.014 +2015-10-17 01:09:12:265155,231,3.1,0.013 +2015-10-17 01:09:12:469124,232,3.2,0.013 +2015-10-17 01:09:12:675091,233,3.3,0.012 +2015-10-17 01:09:12:880806,234,3.4,0.011 +2015-10-17 01:09:12:880806,234,3.4,0.011 +2015-10-17 01:09:13:082930,235,3.5,0.011 +2015-10-17 01:09:13:285099,236,3.6,0.01 +2015-10-17 01:09:13:491619,237,3.7,0.01 +2015-10-17 01:09:13:692632,238,3.8,0.01 +2015-10-17 01:09:13:899633,239,3.9,0.009 +2015-10-17 01:09:13:899633,239,3.9,0.009 +2015-10-17 01:09:14:106257,240,4.0,0.009 +2015-10-17 01:09:14:312725,241,4.1,0.008 +2015-10-17 01:09:14:516634,242,4.2,0.008 +2015-10-17 01:09:14:719816,243,4.3,0.008 +2015-10-17 01:09:14:925967,244,4.4,0.007 +2015-10-17 01:09:14:925967,244,4.4,0.007 +2015-10-17 01:09:15:131989,245,4.5,0.007 +2015-10-17 01:09:15:334357,246,4.6,0.007 +2015-10-17 01:09:15:537003,247,4.7,0.007 +2015-10-17 01:09:15:742435,248,4.8,0.006 +2015-10-17 01:09:15:949086,249,4.9,0.006 +2015-10-17 01:09:15:949086,249,4.9,0.006 +2015-10-17 01:09:16:153599,250,5.0,0.006 +2015-10-17 01:09:16:359133,251,5.1,0.006 +2015-10-17 01:09:16:561062,252,5.2,0.006 +2015-10-17 01:09:16:763118,253,5.3,0.007 +2015-10-17 01:09:16:967849,254,5.4,0.007 +2015-10-17 01:09:16:967849,254,5.4,0.007 +2015-10-17 01:09:17:173851,255,5.5,0.007 +2015-10-17 01:09:17:376817,256,5.6,0.007 +2015-10-17 01:09:17:583100,257,5.7,0.008 +2015-10-17 01:09:17:784624,258,5.8,0.008 +2015-10-17 01:09:17:986485,259,5.9,0.008 +2015-10-17 01:09:17:986485,259,5.9,0.008 +2015-10-17 01:09:18:188419,260,6.0,0.009 +2015-10-17 01:09:18:392844,261,6.1,0.009 +2015-10-17 01:09:18:599097,262,6.2,0.01 +2015-10-17 01:09:18:804582,263,6.3,0.01 +2015-10-17 01:09:19:007190,264,6.4,0.01 +2015-10-17 01:09:19:007190,264,6.4,0.01 +2015-10-17 01:09:19:209311,265,6.5,0.011 +2015-10-17 01:09:19:415297,266,6.6,0.011 +2015-10-17 01:09:19:621963,267,6.7,0.012 +2015-10-17 01:09:19:824362,268,6.8,0.013 +2015-10-17 01:09:20:025173,269,6.9,0.013 +2015-10-17 01:09:20:025173,269,6.9,0.013 +2015-10-17 01:09:20:229321,270,7.0,0.014 +2015-10-17 01:09:20:436335,271,7.1,0.015 +2015-10-17 01:09:20:641376,272,7.2,0.016 +2015-10-17 01:09:20:847479,273,7.3,0.017 +2015-10-17 01:09:21:054078,274,7.4,0.018 +2015-10-17 01:09:21:054078,274,7.4,0.018 +2015-10-17 01:09:21:255338,275,7.5,0.019 +2015-10-17 01:09:21:458858,276,7.6,0.02 +2015-10-17 01:09:21:663583,277,7.7,0.022 +2015-10-17 01:09:21:864914,278,7.8,0.023 +2015-10-17 01:09:22:066288,279,7.9,0.025 +2015-10-17 01:09:22:066288,279,7.9,0.025 +2015-10-17 01:09:22:272567,280,8.0,0.027 +2015-10-17 01:09:22:476943,281,8.1,0.029 +2015-10-17 01:09:22:680470,282,8.2,0.031 +2015-10-17 01:09:22:887260,283,8.3,0.034 +2015-10-17 01:09:23:093561,284,8.4,0.037 +2015-10-17 01:09:23:093561,284,8.4,0.037 +2015-10-17 01:09:23:296585,285,8.5,0.04 +2015-10-17 01:09:23:499690,286,8.6,0.044 +2015-10-17 01:09:23:701518,287,8.7,0.048 +2015-10-17 01:09:23:905838,288,8.8,0.053 +2015-10-17 01:09:24:108553,289,8.9,0.058 +2015-10-17 01:09:24:108553,289,8.9,0.058 +2015-10-17 01:09:24:311527,290,9.0,0.064 +2015-10-17 01:09:24:517398,291,9.1,0.071 +2015-10-17 01:09:24:719160,292,9.2,0.077 +2015-10-17 01:09:24:921022,293,9.3,0.085 +2015-10-17 01:09:25:127167,294,9.4,0.092 +2015-10-17 01:09:25:127167,294,9.4,0.092 +2015-10-17 01:09:25:333755,295,9.5,0.099 +2015-10-17 01:09:25:536073,296,9.6,0.106 +2015-10-17 01:09:25:742501,297,9.7,0.111 +2015-10-17 01:09:25:949489,298,9.8,0.115 +2015-10-17 01:09:26:155441,299,9.9,0.117 +2015-10-17 01:09:26:155441,299,9.9,0.117 +2015-10-17 01:09:26:359266,300,10.0,0.117 +2015-10-17 01:09:26:561160,301,10.1,0.117 +2015-10-17 01:09:26:767659,302,10.2,0.115 +2015-10-17 01:09:26:973226,303,10.3,0.111 +2015-10-17 01:09:27:177553,304,10.4,0.106 +2015-10-17 01:09:27:177553,304,10.4,0.106 +2015-10-17 01:09:27:382031,305,10.5,0.099 +2015-10-17 01:09:27:587624,306,10.6,0.092 +2015-10-17 01:09:27:792568,307,10.7,0.085 +2015-10-17 01:09:27:999433,308,10.8,0.077 +2015-10-17 01:09:28:204092,309,10.9,0.071 +2015-10-17 01:09:28:204092,309,10.9,0.071 +2015-10-17 01:09:28:409817,310,11.0,0.064 +2015-10-17 01:09:28:614008,311,11.1,0.058 +2015-10-17 01:09:28:815294,312,11.2,0.053 +2015-10-17 01:09:29:018455,313,11.3,0.048 +2015-10-17 01:09:29:220715,314,11.4,0.044 +2015-10-17 01:09:29:220715,314,11.4,0.044 +2015-10-17 01:09:29:423566,315,11.5,0.04 +2015-10-17 01:09:29:626374,316,11.6,0.037 +2015-10-17 01:09:29:832680,317,11.7,0.034 +2015-10-17 01:09:30:034018,318,11.8,0.031 +2015-10-17 01:09:30:240523,319,11.9,0.029 +2015-10-17 01:09:30:441956,320,12.0,0.027 +2015-10-17 01:09:30:441956,320,12.0,0.027 +2015-10-17 01:09:30:643096,321,12.1,0.025 +2015-10-17 01:09:30:847075,322,12.2,0.023 +2015-10-17 01:09:31:051599,323,12.3,0.022 +2015-10-17 01:09:31:253968,324,12.4,0.02 +2015-10-17 01:09:31:253968,324,12.4,0.02 +2015-10-17 01:09:31:459898,325,12.5,0.019 +2015-10-17 01:09:31:663248,326,12.6,0.018 +2015-10-17 01:09:31:865894,327,12.7,0.017 +2015-10-17 01:09:32:067924,328,12.8,0.016 +2015-10-17 01:09:32:274281,329,12.9,0.015 +2015-10-17 01:09:32:274281,329,12.9,0.015 +2015-10-17 01:09:32:479515,330,13.0,0.014 +2015-10-17 01:09:32:682030,331,13.1,0.013 +2015-10-17 01:09:32:884778,332,13.2,0.013 +2015-10-17 01:09:33:086234,333,13.3,0.012 +2015-10-17 01:09:33:292639,334,13.4,0.011 +2015-10-17 01:09:33:292639,334,13.4,0.011 +2015-10-17 01:09:33:494311,335,13.5,0.011 +2015-10-17 01:09:33:696573,336,13.6,0.01 +2015-10-17 01:09:33:901810,337,13.7,0.01 +2015-10-17 01:09:34:106535,338,13.8,0.01 +2015-10-17 01:09:34:310223,339,13.9,0.009 +2015-10-17 01:09:34:310223,339,13.9,0.009 +2015-10-17 01:09:34:511892,340,14.0,0.009 +2015-10-17 01:09:34:718833,341,14.1,0.008 +2015-10-17 01:09:34:925275,342,14.2,0.008 +2015-10-17 01:09:35:128402,343,14.3,0.008 +2015-10-17 01:09:35:330416,344,14.4,0.007 +2015-10-17 01:09:35:330416,344,14.4,0.007 +2015-10-17 01:09:35:536595,345,14.5,0.007 +2015-10-17 01:09:35:739464,346,14.6,0.007 +2015-10-17 01:09:35:940828,347,14.7,0.007 +2015-10-17 01:09:36:146485,348,14.8,0.006 +2015-10-17 01:09:36:351402,349,14.9,0.006 +2015-10-17 01:09:36:351402,349,14.9,0.006 +2015-10-17 01:09:36:555006,350,15.0,0.006 +2015-10-17 01:09:36:755922,351,15.1,0.006 +2015-10-17 01:09:36:960180,352,15.2,0.006 +2015-10-17 01:09:37:160418,353,15.3,0.007 +2015-10-17 01:09:37:364479,354,15.4,0.007 +2015-10-17 01:09:37:364479,354,15.4,0.007 +2015-10-17 01:09:37:570445,355,15.5,0.007 +2015-10-17 01:09:37:772689,356,15.6,0.007 +2015-10-17 01:09:37:973714,357,15.7,0.008 +2015-10-17 01:09:38:180522,358,15.8,0.008 +2015-10-17 01:09:38:383090,359,15.9,0.008 +2015-10-17 01:09:38:383090,359,15.9,0.008 +2015-10-17 01:09:38:588733,360,16.0,0.009 +2015-10-17 01:09:38:792109,361,16.1,0.009 +2015-10-17 01:09:38:999489,362,16.2,0.01 +2015-10-17 01:09:39:201193,363,16.3,0.01 +2015-10-17 01:09:39:402575,364,16.4,0.01 +2015-10-17 01:09:39:402575,364,16.4,0.01 +2015-10-17 01:09:39:606547,365,16.5,0.011 +2015-10-17 01:09:39:810950,366,16.6,0.011 +2015-10-17 01:09:40:016688,367,16.7,0.012 +2015-10-17 01:09:40:223113,368,16.8,0.013 +2015-10-17 01:09:40:427113,369,16.9,0.013 +2015-10-17 01:09:40:628886,370,17.0,0.014 +2015-10-17 01:09:40:628886,370,17.0,0.014 +2015-10-17 01:09:40:835211,371,17.1,0.015 +2015-10-17 01:09:41:040904,372,17.2,0.016 +2015-10-17 01:09:41:242244,373,17.3,0.017 +2015-10-17 01:09:41:445403,374,17.4,0.018 +2015-10-17 01:09:41:445403,374,17.4,0.018 +2015-10-17 01:09:41:651107,375,17.5,0.019 +2015-10-17 01:09:41:853971,376,17.6,0.02 +2015-10-17 01:09:42:056371,377,17.7,0.022 +2015-10-17 01:09:42:258091,378,17.8,0.023 +2015-10-17 01:09:42:460295,379,17.9,0.025 +2015-10-17 01:09:42:460295,379,17.9,0.025 +2015-10-17 01:09:42:663557,380,18.0,0.027 +2015-10-17 01:09:42:868999,381,18.1,0.029 +2015-10-17 01:09:43:071242,382,18.2,0.031 +2015-10-17 01:09:43:277060,383,18.3,0.034 +2015-10-17 01:09:43:482980,384,18.4,0.037 +2015-10-17 01:09:43:482980,384,18.4,0.037 +2015-10-17 01:09:43:687364,385,18.5,0.04 +2015-10-17 01:09:43:888969,386,18.6,0.044 +2015-10-17 01:09:44:092695,387,18.7,0.048 +2015-10-17 01:09:44:294025,388,18.8,0.053 +2015-10-17 01:09:44:497405,389,18.9,0.058 +2015-10-17 01:09:44:497405,389,18.9,0.058 +2015-10-17 01:09:44:699218,390,19.0,0.064 +2015-10-17 01:09:44:902956,391,19.1,0.071 +2015-10-17 01:09:45:105514,392,19.2,0.077 +2015-10-17 01:09:45:307991,393,19.3,0.085 +2015-10-17 01:09:45:514537,394,19.4,0.092 +2015-10-17 01:09:45:514537,394,19.4,0.092 +2015-10-17 01:09:45:719667,395,19.5,0.099 +2015-10-17 01:09:45:922041,396,19.6,0.106 +2015-10-17 01:09:46:126713,397,19.7,0.111 +2015-10-17 01:09:46:333146,398,19.8,0.115 +2015-10-17 01:09:46:539082,399,19.9,0.117 diff --git a/toymodel.py b/toymodel.py new file mode 100644 index 000000000000..590cf88c0f1e --- /dev/null +++ b/toymodel.py @@ -0,0 +1,106 @@ +# code for example notebook + +import math + +from qcodes.instrument.mock import MockInstrument +from qcodes.utils.validators import Numbers + + +# here's a toy model - models mimic a communication channel, +# accepting and returning string data, so that they can +# mimic real instruments as closely as possible +# +# This could certainly be built in for simple subclassing +# if we use it a lot +class ModelError(Exception): + pass + + +class AModel(object): + def __init__(self): + self._gates = [0.0, 0.0, 0.0] + self._excitation = 0.1 + + def _output(self): + # my super exciting model! + # make a nice pattern that looks sort of double-dotty + # with the first two gates controlling the two dots, + # and the third looking like Vsd + delta_i = 10 + delta_j = 10 + di = (self._gates[0] + delta_i / 2) % delta_i - delta_i / 2 + dj = (self._gates[1] + delta_j / 2) % delta_j - delta_j / 2 + vsd = math.sqrt(self._gates[2]**2 + self._excitation**2) + dij = math.sqrt(di**2 + dj**2) - vsd + g = (vsd**2 + 1) * (1 / (dij**2 + 1) + + 0.1 * (math.atan(-dij) + math.pi / 2)) + return g + + def write(self, instrument, parameter, value): + if instrument == 'gates' and parameter[0] == 'c': + self._gates[int(parameter[1:])] = float(value) + elif instrument == 'gates' and parameter == 'rst': + self._gates = [0.0, 0.0, 0.0] + elif instrument == 'source' and parameter == 'ampl': + self._excitation = float(value) + else: + raise ModelError('unrecognized write {}, {}, {}'.format( + instrument, parameter, value)) + + def ask(self, instrument, parameter): + gates = self._gates + + if instrument == 'gates' and parameter[0] == 'c': + v = gates[int(parameter[1:])] + elif instrument == 'source' and parameter == 'ampl': + v = self._excitation + elif instrument == 'meter' and parameter == 'ampl': + # here's my super complex model output! + v = self._output() * self._excitation + else: + raise ModelError('unrecognized ask {}, {}'.format( + instrument, parameter)) + + return '{:.3f}'.format(v) + + +# make our mock instruments +# real instruments would subclass IPInstrument or VisaInstrument +# instead of MockInstrument, +# and be instantiated with an address rather than a model +class MockGates(MockInstrument): + def __init__(self, name, model): + super().__init__(name, model=model) + + for i in range(3): + cmdbase = 'c{}'.format(i) + self.add_parameter('chan{}'.format(i), + get_cmd=cmdbase + '?', + set_cmd=cmdbase + ' {:.4f}', + parse_function=float, + vals=Numbers(-100, 100)) + + self.add_function('reset', call_cmd='rst') + + +class MockSource(MockInstrument): + def __init__(self, name, model): + super().__init__(name, model=model) + + # this parameter uses built-in sweeping to change slowly + self.add_parameter('amplitude', + get_cmd='ampl?', + set_cmd='ampl {:.4f}', + parse_function=float, + vals=Numbers(0, 10), + sweep_step=0.1, + sweep_delay=0.05) + + +class MockMeter(MockInstrument): + def __init__(self, name, model): + super().__init__(name, model=model) + + self.add_parameter('amplitude', + get_cmd='ampl?', + parse_function=float)