{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# 13. Streaming data from Arduino\n", "\n", "
" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", "
\n", " \n", " Loading BokehJS ...\n", "
" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "application/javascript": [ "\n", "(function(root) {\n", " function now() {\n", " return new Date();\n", " }\n", "\n", " var force = true;\n", "\n", " if (typeof root._bokeh_onload_callbacks === \"undefined\" || force === true) {\n", " root._bokeh_onload_callbacks = [];\n", " root._bokeh_is_loading = undefined;\n", " }\n", "\n", " var JS_MIME_TYPE = 'application/javascript';\n", " var HTML_MIME_TYPE = 'text/html';\n", " var EXEC_MIME_TYPE = 'application/vnd.bokehjs_exec.v0+json';\n", " var CLASS_NAME = 'output_bokeh rendered_html';\n", "\n", " /**\n", " * Render data to the DOM node\n", " */\n", " function render(props, node) {\n", " var script = document.createElement(\"script\");\n", " node.appendChild(script);\n", " }\n", "\n", " /**\n", " * Handle when an output is cleared or removed\n", " */\n", " function handleClearOutput(event, handle) {\n", " var cell = handle.cell;\n", "\n", " var id = cell.output_area._bokeh_element_id;\n", " var server_id = cell.output_area._bokeh_server_id;\n", " // Clean up Bokeh references\n", " if (id != null && id in Bokeh.index) {\n", " Bokeh.index[id].model.document.clear();\n", " delete Bokeh.index[id];\n", " }\n", "\n", " if (server_id !== undefined) {\n", " // Clean up Bokeh references\n", " var cmd = \"from bokeh.io.state import curstate; print(curstate().uuid_to_server['\" + server_id + \"'].get_sessions()[0].document.roots[0]._id)\";\n", " cell.notebook.kernel.execute(cmd, {\n", " iopub: {\n", " output: function(msg) {\n", " var id = msg.content.text.trim();\n", " if (id in Bokeh.index) {\n", " Bokeh.index[id].model.document.clear();\n", " delete Bokeh.index[id];\n", " }\n", " }\n", " }\n", " });\n", " // Destroy server and session\n", " var cmd = \"import bokeh.io.notebook as ion; ion.destroy_server('\" + server_id + \"')\";\n", " cell.notebook.kernel.execute(cmd);\n", " }\n", " }\n", "\n", " /**\n", " * Handle when a new output is added\n", " */\n", " function handleAddOutput(event, handle) {\n", " var output_area = handle.output_area;\n", " var output = handle.output;\n", "\n", " // limit handleAddOutput to display_data with EXEC_MIME_TYPE content only\n", " if ((output.output_type != \"display_data\") || (!output.data.hasOwnProperty(EXEC_MIME_TYPE))) {\n", " return\n", " }\n", "\n", " var toinsert = output_area.element.find(\".\" + CLASS_NAME.split(' ')[0]);\n", "\n", " if (output.metadata[EXEC_MIME_TYPE][\"id\"] !== undefined) {\n", " toinsert[toinsert.length - 1].firstChild.textContent = output.data[JS_MIME_TYPE];\n", " // store reference to embed id on output_area\n", " output_area._bokeh_element_id = output.metadata[EXEC_MIME_TYPE][\"id\"];\n", " }\n", " if (output.metadata[EXEC_MIME_TYPE][\"server_id\"] !== undefined) {\n", " var bk_div = document.createElement(\"div\");\n", " bk_div.innerHTML = output.data[HTML_MIME_TYPE];\n", " var script_attrs = bk_div.children[0].attributes;\n", " for (var i = 0; i < script_attrs.length; i++) {\n", " toinsert[toinsert.length - 1].firstChild.setAttribute(script_attrs[i].name, script_attrs[i].value);\n", " toinsert[toinsert.length - 1].firstChild.textContent = bk_div.children[0].textContent\n", " }\n", " // store reference to server id on output_area\n", " output_area._bokeh_server_id = output.metadata[EXEC_MIME_TYPE][\"server_id\"];\n", " }\n", " }\n", "\n", " function register_renderer(events, OutputArea) {\n", "\n", " function append_mime(data, metadata, element) {\n", " // create a DOM node to render to\n", " var toinsert = this.create_output_subarea(\n", " metadata,\n", " CLASS_NAME,\n", " EXEC_MIME_TYPE\n", " );\n", " this.keyboard_manager.register_events(toinsert);\n", " // Render to node\n", " var props = {data: data, metadata: metadata[EXEC_MIME_TYPE]};\n", " render(props, toinsert[toinsert.length - 1]);\n", " element.append(toinsert);\n", " return toinsert\n", " }\n", "\n", " /* Handle when an output is cleared or removed */\n", " events.on('clear_output.CodeCell', handleClearOutput);\n", " events.on('delete.Cell', handleClearOutput);\n", "\n", " /* Handle when a new output is added */\n", " events.on('output_added.OutputArea', handleAddOutput);\n", "\n", " /**\n", " * Register the mime type and append_mime function with output_area\n", " */\n", " OutputArea.prototype.register_mime_type(EXEC_MIME_TYPE, append_mime, {\n", " /* Is output safe? */\n", " safe: true,\n", " /* Index of renderer in `output_area.display_order` */\n", " index: 0\n", " });\n", " }\n", "\n", " // register the mime type if in Jupyter Notebook environment and previously unregistered\n", " if (root.Jupyter !== undefined) {\n", " var events = require('base/js/events');\n", " var OutputArea = require('notebook/js/outputarea').OutputArea;\n", "\n", " if (OutputArea.prototype.mime_types().indexOf(EXEC_MIME_TYPE) == -1) {\n", " register_renderer(events, OutputArea);\n", " }\n", " }\n", "\n", " \n", " if (typeof (root._bokeh_timeout) === \"undefined\" || force === true) {\n", " root._bokeh_timeout = Date.now() + 5000;\n", " root._bokeh_failed_load = false;\n", " }\n", "\n", " var NB_LOAD_WARNING = {'data': {'text/html':\n", " \"
\\n\"+\n", " \"

\\n\"+\n", " \"BokehJS does not appear to have successfully loaded. If loading BokehJS from CDN, this \\n\"+\n", " \"may be due to a slow or bad network connection. Possible fixes:\\n\"+\n", " \"

\\n\"+\n", " \"\\n\"+\n", " \"\\n\"+\n", " \"from bokeh.resources import INLINE\\n\"+\n", " \"output_notebook(resources=INLINE)\\n\"+\n", " \"\\n\"+\n", " \"
\"}};\n", "\n", " function display_loaded() {\n", " var el = document.getElementById(\"1001\");\n", " if (el != null) {\n", " el.textContent = \"BokehJS is loading...\";\n", " }\n", " if (root.Bokeh !== undefined) {\n", " if (el != null) {\n", " el.textContent = \"BokehJS \" + root.Bokeh.version + \" successfully loaded.\";\n", " }\n", " } else if (Date.now() < root._bokeh_timeout) {\n", " setTimeout(display_loaded, 100)\n", " }\n", " }\n", "\n", "\n", " function run_callbacks() {\n", " try {\n", " root._bokeh_onload_callbacks.forEach(function(callback) {\n", " if (callback != null)\n", " callback();\n", " });\n", " } finally {\n", " delete root._bokeh_onload_callbacks\n", " }\n", " console.debug(\"Bokeh: all callbacks have finished\");\n", " }\n", "\n", " function load_libs(css_urls, js_urls, callback) {\n", " if (css_urls == null) css_urls = [];\n", " if (js_urls == null) js_urls = [];\n", "\n", " root._bokeh_onload_callbacks.push(callback);\n", " if (root._bokeh_is_loading > 0) {\n", " console.debug(\"Bokeh: BokehJS is being loaded, scheduling callback at\", now());\n", " return null;\n", " }\n", " if (js_urls == null || js_urls.length === 0) {\n", " run_callbacks();\n", " return null;\n", " }\n", " console.debug(\"Bokeh: BokehJS not loaded, scheduling load and callback at\", now());\n", " root._bokeh_is_loading = css_urls.length + js_urls.length;\n", "\n", " function on_load() {\n", " root._bokeh_is_loading--;\n", " if (root._bokeh_is_loading === 0) {\n", " console.debug(\"Bokeh: all BokehJS libraries/stylesheets loaded\");\n", " run_callbacks()\n", " }\n", " }\n", "\n", " function on_error() {\n", " console.error(\"failed to load \" + url);\n", " }\n", "\n", " for (var i = 0; i < css_urls.length; i++) {\n", " var url = css_urls[i];\n", " const element = document.createElement(\"link\");\n", " element.onload = on_load;\n", " element.onerror = on_error;\n", " element.rel = \"stylesheet\";\n", " element.type = \"text/css\";\n", " element.href = url;\n", " console.debug(\"Bokeh: injecting link tag for BokehJS stylesheet: \", url);\n", " document.body.appendChild(element);\n", " }\n", "\n", " const hashes = {\"https://cdn.bokeh.org/bokeh/release/bokeh-2.2.1.min.js\": \"qkRvDQVAIfzsJo40iRBbxt6sttt0hv4lh74DG7OK4MCHv4C5oohXYoHUM5W11uqS\", \"https://cdn.bokeh.org/bokeh/release/bokeh-widgets-2.2.1.min.js\": \"Sb7Mr06a9TNlet/GEBeKaf5xH3eb6AlCzwjtU82wNPyDrnfoiVl26qnvlKjmcAd+\", \"https://cdn.bokeh.org/bokeh/release/bokeh-tables-2.2.1.min.js\": \"HaJ15vgfmcfRtB4c4YBOI4f1MUujukqInOWVqZJZZGK7Q+ivud0OKGSTn/Vm2iso\"};\n", "\n", " for (var i = 0; i < js_urls.length; i++) {\n", " var url = js_urls[i];\n", " var element = document.createElement('script');\n", " element.onload = on_load;\n", " element.onerror = on_error;\n", " element.async = false;\n", " element.src = url;\n", " if (url in hashes) {\n", " element.crossOrigin = \"anonymous\";\n", " element.integrity = \"sha384-\" + hashes[url];\n", " }\n", " console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n", " document.head.appendChild(element);\n", " }\n", " };\n", "\n", " function inject_raw_css(css) {\n", " const element = document.createElement(\"style\");\n", " element.appendChild(document.createTextNode(css));\n", " document.body.appendChild(element);\n", " }\n", "\n", " \n", " var js_urls = [\"https://cdn.bokeh.org/bokeh/release/bokeh-2.2.1.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-widgets-2.2.1.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-tables-2.2.1.min.js\"];\n", " var css_urls = [];\n", " \n", "\n", " var inline_js = [\n", " function(Bokeh) {\n", " Bokeh.set_log_level(\"info\");\n", " },\n", " function(Bokeh) {\n", " \n", " \n", " }\n", " ];\n", "\n", " function run_inline_js() {\n", " \n", " if (root.Bokeh !== undefined || force === true) {\n", " \n", " for (var i = 0; i < inline_js.length; i++) {\n", " inline_js[i].call(root, root.Bokeh);\n", " }\n", " if (force === true) {\n", " display_loaded();\n", " }} else if (Date.now() < root._bokeh_timeout) {\n", " setTimeout(run_inline_js, 100);\n", " } else if (!root._bokeh_failed_load) {\n", " console.log(\"Bokeh: BokehJS failed to load within specified timeout.\");\n", " root._bokeh_failed_load = true;\n", " } else if (force !== true) {\n", " var cell = $(document.getElementById(\"1001\")).parents('.cell').data().cell;\n", " cell.output_area.append_execute_result(NB_LOAD_WARNING)\n", " }\n", "\n", " }\n", "\n", " if (root._bokeh_is_loading === 0) {\n", " console.debug(\"Bokeh: BokehJS loaded, going straight to plotting\");\n", " run_inline_js();\n", " } else {\n", " load_libs(css_urls, js_urls, function() {\n", " console.debug(\"Bokeh: BokehJS plotting callback run at\", now());\n", " run_inline_js();\n", " });\n", " }\n", "}(window));" ], "application/vnd.bokehjs_load.v0+json": "\n(function(root) {\n function now() {\n return new Date();\n }\n\n var force = true;\n\n if (typeof root._bokeh_onload_callbacks === \"undefined\" || force === true) {\n root._bokeh_onload_callbacks = [];\n root._bokeh_is_loading = undefined;\n }\n\n \n\n \n if (typeof (root._bokeh_timeout) === \"undefined\" || force === true) {\n root._bokeh_timeout = Date.now() + 5000;\n root._bokeh_failed_load = false;\n }\n\n var NB_LOAD_WARNING = {'data': {'text/html':\n \"
\\n\"+\n \"

\\n\"+\n \"BokehJS does not appear to have successfully loaded. If loading BokehJS from CDN, this \\n\"+\n \"may be due to a slow or bad network connection. Possible fixes:\\n\"+\n \"

\\n\"+\n \"\\n\"+\n \"\\n\"+\n \"from bokeh.resources import INLINE\\n\"+\n \"output_notebook(resources=INLINE)\\n\"+\n \"\\n\"+\n \"
\"}};\n\n function display_loaded() {\n var el = document.getElementById(\"1001\");\n if (el != null) {\n el.textContent = \"BokehJS is loading...\";\n }\n if (root.Bokeh !== undefined) {\n if (el != null) {\n el.textContent = \"BokehJS \" + root.Bokeh.version + \" successfully loaded.\";\n }\n } else if (Date.now() < root._bokeh_timeout) {\n setTimeout(display_loaded, 100)\n }\n }\n\n\n function run_callbacks() {\n try {\n root._bokeh_onload_callbacks.forEach(function(callback) {\n if (callback != null)\n callback();\n });\n } finally {\n delete root._bokeh_onload_callbacks\n }\n console.debug(\"Bokeh: all callbacks have finished\");\n }\n\n function load_libs(css_urls, js_urls, callback) {\n if (css_urls == null) css_urls = [];\n if (js_urls == null) js_urls = [];\n\n root._bokeh_onload_callbacks.push(callback);\n if (root._bokeh_is_loading > 0) {\n console.debug(\"Bokeh: BokehJS is being loaded, scheduling callback at\", now());\n return null;\n }\n if (js_urls == null || js_urls.length === 0) {\n run_callbacks();\n return null;\n }\n console.debug(\"Bokeh: BokehJS not loaded, scheduling load and callback at\", now());\n root._bokeh_is_loading = css_urls.length + js_urls.length;\n\n function on_load() {\n root._bokeh_is_loading--;\n if (root._bokeh_is_loading === 0) {\n console.debug(\"Bokeh: all BokehJS libraries/stylesheets loaded\");\n run_callbacks()\n }\n }\n\n function on_error() {\n console.error(\"failed to load \" + url);\n }\n\n for (var i = 0; i < css_urls.length; i++) {\n var url = css_urls[i];\n const element = document.createElement(\"link\");\n element.onload = on_load;\n element.onerror = on_error;\n element.rel = \"stylesheet\";\n element.type = \"text/css\";\n element.href = url;\n console.debug(\"Bokeh: injecting link tag for BokehJS stylesheet: \", url);\n document.body.appendChild(element);\n }\n\n const hashes = {\"https://cdn.bokeh.org/bokeh/release/bokeh-2.2.1.min.js\": \"qkRvDQVAIfzsJo40iRBbxt6sttt0hv4lh74DG7OK4MCHv4C5oohXYoHUM5W11uqS\", \"https://cdn.bokeh.org/bokeh/release/bokeh-widgets-2.2.1.min.js\": \"Sb7Mr06a9TNlet/GEBeKaf5xH3eb6AlCzwjtU82wNPyDrnfoiVl26qnvlKjmcAd+\", \"https://cdn.bokeh.org/bokeh/release/bokeh-tables-2.2.1.min.js\": \"HaJ15vgfmcfRtB4c4YBOI4f1MUujukqInOWVqZJZZGK7Q+ivud0OKGSTn/Vm2iso\"};\n\n for (var i = 0; i < js_urls.length; i++) {\n var url = js_urls[i];\n var element = document.createElement('script');\n element.onload = on_load;\n element.onerror = on_error;\n element.async = false;\n element.src = url;\n if (url in hashes) {\n element.crossOrigin = \"anonymous\";\n element.integrity = \"sha384-\" + hashes[url];\n }\n console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n document.head.appendChild(element);\n }\n };\n\n function inject_raw_css(css) {\n const element = document.createElement(\"style\");\n element.appendChild(document.createTextNode(css));\n document.body.appendChild(element);\n }\n\n \n var js_urls = [\"https://cdn.bokeh.org/bokeh/release/bokeh-2.2.1.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-widgets-2.2.1.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-tables-2.2.1.min.js\"];\n var css_urls = [];\n \n\n var inline_js = [\n function(Bokeh) {\n Bokeh.set_log_level(\"info\");\n },\n function(Bokeh) {\n \n \n }\n ];\n\n function run_inline_js() {\n \n if (root.Bokeh !== undefined || force === true) {\n \n for (var i = 0; i < inline_js.length; i++) {\n inline_js[i].call(root, root.Bokeh);\n }\n if (force === true) {\n display_loaded();\n }} else if (Date.now() < root._bokeh_timeout) {\n setTimeout(run_inline_js, 100);\n } else if (!root._bokeh_failed_load) {\n console.log(\"Bokeh: BokehJS failed to load within specified timeout.\");\n root._bokeh_failed_load = true;\n } else if (force !== true) {\n var cell = $(document.getElementById(\"1001\")).parents('.cell').data().cell;\n cell.output_area.append_execute_result(NB_LOAD_WARNING)\n }\n\n }\n\n if (root._bokeh_is_loading === 0) {\n console.debug(\"Bokeh: BokehJS loaded, going straight to plotting\");\n run_inline_js();\n } else {\n load_libs(css_urls, js_urls, function() {\n console.debug(\"Bokeh: BokehJS plotting callback run at\", now());\n run_inline_js();\n });\n }\n}(window));" }, "metadata": {}, "output_type": "display_data" } ], "source": [ "import time\n", "\n", "import numpy as np\n", "import pandas as pd\n", "\n", "import serial\n", "import serial.tools.list_ports\n", "\n", "import bokeh.plotting\n", "import bokeh.io\n", "bokeh.io.output_notebook()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "\n", "As we will need to do when we need to connect to Arduino using Python, we will begin this notebook with utility functions. (Maybe I should have put these in a package, but, again, I leave them like this since you may want to modify them to match whatever sketch is loaded on to your Arduino Uno, or maybe I will.)" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "def find_arduino(port=None):\n", " \"\"\"Get the name of the port that is connected to Arduino.\"\"\"\n", " if port is None:\n", " ports = serial.tools.list_ports.comports()\n", " for p in ports:\n", " if p.manufacturer is not None and \"Arduino\" in p.manufacturer:\n", " port = p.device\n", " return port\n", "\n", "\n", "def handshake_arduino(\n", " arduino, sleep_time=1, print_handshake_message=False, handshake_code=0\n", "):\n", " \"\"\"Make sure connection is established by sending\n", " and receiving bytes.\"\"\"\n", " # Close and reopen\n", " arduino.close()\n", " arduino.open()\n", "\n", " # Chill out while everything gets set\n", " time.sleep(sleep_time)\n", "\n", " # Set a long timeout to complete handshake\n", " timeout = arduino.timeout\n", " arduino.timeout = 2\n", " \n", " # Read and discard everything that may be in the input buffer\n", " _ = arduino.read_all()\n", "\n", " # Send request to Arduino\n", " arduino.write(bytes([handshake_code]))\n", "\n", " # Read in what Arduino sent\n", " handshake_message = arduino.read_until()\n", "\n", " # Send and receive request again\n", " arduino.write(bytes([handshake_code]))\n", " handshake_message = arduino.read_until()\n", "\n", " # Print the handshake message, if desired\n", " if print_handshake_message:\n", " print(\"Handshake message: \" + handshake_message.decode())\n", "\n", " # Reset the timeout\n", " arduino.timeout = timeout" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Problems with on-demand data\n", "\n", "In many applications, we want to push a button and then have Arduino respond by sending us some data. This is the case when you build your spectrophotometer. You will put in the cuvette, press a button, and then get the measurement. The timing of the measurement, certainly down to the millisecond, is not important. In that case, ask-and-receive, like we did in the last lesson, works fine.\n", "\n", "In other applications, though, we want a steady stream of data, and we want it at well-defined time intervals. In this case, the variation in time between samples that we saw in the ask-and-receive style of the last lesson can be problematic. If our time interval between samples is long, say hundreds of millseconds or more, then it is not really a problem, but even with 20 ms between samples, we already saw that we can be pretty far off.\n", "\n", "A better option is to have Arduino do all the timing and then automatically send data to your computer over serial communication. That is, data **streams** from the board and is constantly collected by the Python interpreter. In this lesson, we will learn how to collect streaming data from Arduino. We will use the same setup as the previous lesson, shown below.\n", "\n", "
\n", " \n", "![Arduino data transfer schematic](arduino_data_transfer_schem.svg)\n", " \n", "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "\n", "## Follow-along exercise 10: Streaming data\n", "\n", "Our sketch is more involved this time, since we are going to have richer communications with Arduino. We want to turn streaming on and off. A convenient way to do this while still preserving access to the on-demand way of receiving data we set up in the previous lessons, is to have two data acquisition (DAQ) modes, on-request and stream. Streaming mode has another parameter, which is the delay between acquisitions. So, we need to allow for the user to input the delay (in milliseconds).\n", "\n", "```arduino\n", "const int voltagePin = A0;\n", "\n", "const int HANDSHAKE = 0;\n", "const int VOLTAGE_REQUEST = 1;\n", "const int ON_REQUEST = 2;\n", "const int STREAM = 3;\n", "const int READ_DAQ_DELAY = 4;\n", "\n", "// Initially, only send data upon request\n", "int daqMode = ON_REQUEST;\n", "\n", "// Default time between data acquisition is 100 ms\n", "int daqDelay = 100;\n", "\n", "// String to store input of DAQ delay\n", "String daqDelayStr;\n", "\n", "\n", "// Keep track of last data acquistion for delays\n", "unsigned long timeOfLastDAQ = 0;\n", "\n", "\n", "unsigned long printVoltage() {\n", " // Read value from analog pin\n", " int value = analogRead(voltagePin);\n", "\n", " // Get the time point\n", " unsigned long timeMilliseconds = millis();\n", "\n", " // Write the result\n", " if (Serial.availableForWrite()) {\n", " String outstr = String(String(timeMilliseconds, DEC) + \",\" + String(value, DEC));\n", " Serial.println(outstr);\n", " }\n", "\n", " // Return time of acquisition\n", " return timeMilliseconds;\n", "}\n", "\n", "\n", "void setup() {\n", " // Initialize serial communication\n", " Serial.begin(115200);\n", "}\n", "\n", "\n", "void loop() { \n", " // If we're streaming\n", " if (daqMode == STREAM) {\n", " if (millis() - timeOfLastDAQ >= daqDelay) {\n", " timeOfLastDAQ = printVoltage();\n", " }\n", " }\n", " \n", " // Check if data has been sent to Arduino and respond accordingly\n", " if (Serial.available() > 0) {\n", " // Read in request\n", " int inByte = Serial.read();\n", "\n", " // If data is requested, fetch it and write it, or handshake\n", " switch(inByte) {\n", " case VOLTAGE_REQUEST:\n", " timeOfLastDAQ = printVoltage();\n", " break;\n", " case ON_REQUEST:\n", " daqMode = ON_REQUEST;\n", " break;\n", " case STREAM:\n", " daqMode = STREAM;\n", " break;\n", " case READ_DAQ_DELAY:\n", " // Read in delay, knowing it is appended with an x\n", " daqDelayStr = Serial.readStringUntil('x');\n", "\n", " // Convert to int and store\n", " daqDelay = daqDelayStr.toInt();\n", "\n", " break;\n", " case HANDSHAKE:\n", " if (Serial.availableForWrite()) {\n", " Serial.println(\"Message received.\");\n", " }\n", " break;\n", " }\n", " }\n", "}\n", "```\n", "\n", "Some comments on this sketch:\n", "\n", "- Much of the setup is the same as the last lesson, including the `printVoltage()` function. We also have global variables `daqMode`, which specifies whether we are in streaming more or on-demand, and `daqDelay`, which specifies the time between data acquisitions.\n", "- In the `loop()` function, we send data along USB if we are in streaming mode and have waited `daqDelay` or longer.\n", "- We then also check to see if any data has been sent to Arduino from the computer. If so, we again enter into a switch-case, as before.\n", "- If we sent a signal from the computer that we want Arduino to read in a DAQ delay, we use `Serial.readStringUntil()` to read in the string specifying the DAQ delay. We specify that the string ends with `'x'`. This is useful as it ensures that Arduino knows exactly when the data coming over USB ends specification of the delay string. Therefore, on the Python side, we need to make sure to append an `'x'` onto the string giving the DAQ delay that we will convert into a bytes array to send to Arduino.\n", "- We use the `toInt()` method to convert the read in string for DAQ delay into an integer.\n", "\n", "Now that we have our sketch, we can set up our global variables so we have them on the Python side as well." ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "HANDSHAKE = 0\n", "VOLTAGE_REQUEST = 1\n", "ON_REQUEST = 2;\n", "STREAM = 3;\n", "READ_DAQ_DELAY = 4;" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Setting up Python to receive data\n", "\n", "We now need to write some code on the Python side to enable streaming. As usual, the first step is to connect to Arduino with a serial connection. We will open it and leave it open to enable convenient streaming, which is also important to do when we stream in data asynchronously." ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Handshake message: Message received.\n", "\n" ] } ], "source": [ "port = find_arduino()\n", "arduino = serial.Serial(port, baudrate=115200)\n", "handshake_arduino(arduino, handshake_code=HANDSHAKE, print_handshake_message=True)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Since we will again be sending in comma delimited data, we can use the data parser we used last time to convert the string we get from Arduino to a time in milliseconds and a voltage." ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [], "source": [ "def parse_raw(raw):\n", " \"\"\"Parse bytes output from Arduino.\"\"\"\n", " raw = raw.decode()\n", " if raw[-1] != \"\\n\":\n", " raise ValueError(\n", " \"Input must end with newline, otherwise message is incomplete.\"\n", " )\n", "\n", " t, V = raw.rstrip().split(\",\")\n", "\n", " return int(t), int(V) * 5 / 1023" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now, we will write a function to turn on a data stream, collect data from it, and then return the result as a Pandas data frame. The steps toward doing so are as follows:\n", "\n", "1. Send a signal to Arduino that gives the delay in data acquisition. The first byte of this signal must be `READ_DAQ_DELAY`, signifying that the following bytes, up to the character `'x'` give the delay. Then, subsequent bytes give the digits of the DAQ delay, followed by `'x'`.\n", "2. Initialize empty Numpy arrays to receive the data.\n", "3. Tell Arduino to switch to streaming mode.\n", "4. Keep reading in data until we acquire the desired number of data points.\n", "5. Tell Arduino to switch to sending data on request.\n", "6. Return a data frame containing the results.\n", "\n", "We will store the result in a Pandas data frame for convenient use later. (If you are unfamiliar with Pandas, you can check out [this introduction](http://justinbois.github.io/bootcamp/2020/lessons/l17_intro_to_pandas.html)." ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [], "source": [ "def daq_stream(arduino, n_data=100, delay=20):\n", " \"\"\"Obtain `n_data` data points from an Arduino stream\n", " with a delay of `delay` milliseconds between each.\"\"\"\n", " # Specify delay\n", " arduino.write(bytes([READ_DAQ_DELAY]) + (str(delay) + \"x\").encode())\n", "\n", " # Initialize output\n", " time_ms = np.empty(n_data)\n", " voltage = np.empty(n_data)\n", "\n", " # Turn on the stream\n", " arduino.write(bytes([STREAM]))\n", "\n", " # Receive data\n", " i = 0\n", " while i < n_data:\n", " raw = arduino.read_until()\n", "\n", " try:\n", " t, V = parse_raw(raw)\n", " time_ms[i] = t\n", " voltage[i] = V\n", " i += 1\n", " except:\n", " pass\n", "\n", " # Turn off the stream\n", " arduino.write(bytes([ON_REQUEST]))\n", "\n", " return pd.DataFrame({'time (ms)': time_ms, 'voltage (V)': voltage})" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's put this function to use and collect some data! We will collect 1000 data points with 20 millisecond intervals. (This will then take 20 seconds to run.)" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [], "source": [ "df = daq_stream(arduino, n_data=1000, delay=20)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "As we did in the last lesson, we can plot the results." ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", "\n", "\n", "\n", "\n", "\n", "
\n" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "application/javascript": [ "(function(root) {\n", " function embed_document(root) {\n", " \n", " var docs_json = {\"106cabbe-64a8-4458-ac29-e0855767304d\":{\"roots\":{\"references\":[{\"attributes\":{\"below\":[{\"id\":\"1011\"}],\"center\":[{\"id\":\"1014\"},{\"id\":\"1018\"}],\"frame_height\":175,\"frame_width\":500,\"left\":[{\"id\":\"1015\"}],\"renderers\":[{\"id\":\"1037\"}],\"title\":{\"id\":\"1039\"},\"toolbar\":{\"id\":\"1026\"},\"x_range\":{\"id\":\"1003\"},\"x_scale\":{\"id\":\"1007\"},\"y_range\":{\"id\":\"1005\"},\"y_scale\":{\"id\":\"1009\"}},\"id\":\"1002\",\"subtype\":\"Figure\",\"type\":\"Plot\"},{\"attributes\":{\"overlay\":{\"id\":\"1025\"}},\"id\":\"1021\",\"type\":\"BoxZoomTool\"},{\"attributes\":{},\"id\":\"1041\",\"type\":\"BasicTickFormatter\"},{\"attributes\":{\"end\":3551.98,\"start\":3531.999},\"id\":\"1003\",\"type\":\"Range1d\"},{\"attributes\":{},\"id\":\"1043\",\"type\":\"BasicTickFormatter\"},{\"attributes\":{},\"id\":\"1020\",\"type\":\"WheelZoomTool\"},{\"attributes\":{\"line_color\":\"#1f77b4\",\"x\":{\"field\":\"time (sec)\"},\"y\":{\"field\":\"voltage (V)\"}},\"id\":\"1035\",\"type\":\"Line\"},{\"attributes\":{\"axis_label\":\"time (s)\",\"formatter\":{\"id\":\"1043\"},\"ticker\":{\"id\":\"1012\"}},\"id\":\"1011\",\"type\":\"LinearAxis\"},{\"attributes\":{\"active_drag\":\"auto\",\"active_inspect\":\"auto\",\"active_multi\":null,\"active_scroll\":\"auto\",\"active_tap\":\"auto\",\"tools\":[{\"id\":\"1019\"},{\"id\":\"1020\"},{\"id\":\"1021\"},{\"id\":\"1022\"},{\"id\":\"1023\"},{\"id\":\"1024\"}]},\"id\":\"1026\",\"type\":\"Toolbar\"},{\"attributes\":{\"data_source\":{\"id\":\"1033\"},\"glyph\":{\"id\":\"1035\"},\"hover_glyph\":null,\"muted_glyph\":null,\"nonselection_glyph\":{\"id\":\"1036\"},\"selection_glyph\":null,\"view\":{\"id\":\"1038\"}},\"id\":\"1037\",\"type\":\"GlyphRenderer\"},{\"attributes\":{},\"id\":\"1024\",\"type\":\"HelpTool\"},{\"attributes\":{\"source\":{\"id\":\"1033\"}},\"id\":\"1038\",\"type\":\"CDSView\"},{\"attributes\":{},\"id\":\"1023\",\"type\":\"ResetTool\"},{\"attributes\":{},\"id\":\"1012\",\"type\":\"BasicTicker\"},{\"attributes\":{},\"id\":\"1005\",\"type\":\"DataRange1d\"},{\"attributes\":{\"line_alpha\":0.1,\"line_color\":\"#1f77b4\",\"x\":{\"field\":\"time (sec)\"},\"y\":{\"field\":\"voltage (V)\"}},\"id\":\"1036\",\"type\":\"Line\"},{\"attributes\":{\"axis\":{\"id\":\"1011\"},\"ticker\":null},\"id\":\"1014\",\"type\":\"Grid\"},{\"attributes\":{},\"id\":\"1046\",\"type\":\"UnionRenderers\"},{\"attributes\":{\"axis_label\":\"voltage (V)\",\"formatter\":{\"id\":\"1041\"},\"ticker\":{\"id\":\"1016\"}},\"id\":\"1015\",\"type\":\"LinearAxis\"},{\"attributes\":{},\"id\":\"1007\",\"type\":\"LinearScale\"},{\"attributes\":{},\"id\":\"1047\",\"type\":\"Selection\"},{\"attributes\":{\"text\":\"\"},\"id\":\"1039\",\"type\":\"Title\"},{\"attributes\":{\"axis\":{\"id\":\"1015\"},\"dimension\":1,\"ticker\":null},\"id\":\"1018\",\"type\":\"Grid\"},{\"attributes\":{},\"id\":\"1022\",\"type\":\"SaveTool\"},{\"attributes\":{\"data\":{\"index\":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,261,262,263,264,265,266,267,268,269,270,271,272,273,274,275,276,277,278,279,280,281,282,283,284,285,286,287,288,289,290,291,292,293,294,295,296,297,298,299,300,301,302,303,304,305,306,307,308,309,310,311,312,313,314,315,316,317,318,319,320,321,322,323,324,325,326,327,328,329,330,331,332,333,334,335,336,337,338,339,340,341,342,343,344,345,346,347,348,349,350,351,352,353,354,355,356,357,358,359,360,361,362,363,364,365,366,367,368,369,370,371,372,373,374,375,376,377,378,379,380,381,382,383,384,385,386,387,388,389,390,391,392,393,394,395,396,397,398,399,400,401,402,403,404,405,406,407,408,409,410,411,412,413,414,415,416,417,418,419,420,421,422,423,424,425,426,427,428,429,430,431,432,433,434,435,436,437,438,439,440,441,442,443,444,445,446,447,448,449,450,451,452,453,454,455,456,457,458,459,460,461,462,463,464,465,466,467,468,469,470,471,472,473,474,475,476,477,478,479,480,481,482,483,484,485,486,487,488,489,490,491,492,493,494,495,496,497,498,499,500,501,502,503,504,505,506,507,508,509,510,511,512,513,514,515,516,517,518,519,520,521,522,523,524,525,526,527,528,529,530,531,532,533,534,535,536,537,538,539,540,541,542,543,544,545,546,547,548,549,550,551,552,553,554,555,556,557,558,559,560,561,562,563,564,565,566,567,568,569,570,571,572,573,574,575,576,577,578,579,580,581,582,583,584,585,586,587,588,589,590,591,592,593,594,595,596,597,598,599,600,601,602,603,604,605,606,607,608,609,610,611,612,613,614,615,616,617,618,619,620,621,622,623,624,625,626,627,628,629,630,631,632,633,634,635,636,637,638,639,640,641,642,643,644,645,646,647,648,649,650,651,652,653,654,655,656,657,658,659,660,661,662,663,664,665,666,667,668,669,670,671,672,673,674,675,676,677,678,679,680,681,682,683,684,685,686,687,688,689,690,691,692,693,694,695,696,697,698,699,700,701,702,703,704,705,706,707,708,709,710,711,712,713,714,715,716,717,718,719,720,721,722,723,724,725,726,727,728,729,730,731,732,733,734,735,736,737,738,739,740,741,742,743,744,745,746,747,748,749,750,751,752,753,754,755,756,757,758,759,760,761,762,763,764,765,766,767,768,769,770,771,772,773,774,775,776,777,778,779,780,781,782,783,784,785,786,787,788,789,790,791,792,793,794,795,796,797,798,799,800,801,802,803,804,805,806,807,808,809,810,811,812,813,814,815,816,817,818,819,820,821,822,823,824,825,826,827,828,829,830,831,832,833,834,835,836,837,838,839,840,841,842,843,844,845,846,847,848,849,850,851,852,853,854,855,856,857,858,859,860,861,862,863,864,865,866,867,868,869,870,871,872,873,874,875,876,877,878,879,880,881,882,883,884,885,886,887,888,889,890,891,892,893,894,895,896,897,898,899,900,901,902,903,904,905,906,907,908,909,910,911,912,913,914,915,916,917,918,919,920,921,922,923,924,925,926,927,928,929,930,931,932,933,934,935,936,937,938,939,940,941,942,943,944,945,946,947,948,949,950,951,952,953,954,955,956,957,958,959,960,961,962,963,964,965,966,967,968,969,970,971,972,973,974,975,976,977,978,979,980,981,982,983,984,985,986,987,988,989,990,991,992,993,994,995,996,997,998,999],\"time (ms)\":{\"__ndarray__\":\"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\",\"dtype\":\"float64\",\"order\":\"little\",\"shape\":[1000]},\"time (sec)\":{\"__ndarray__\":\"aJHtfP+Xq0A/NV66CZirQBfZzvcTmKtA7nw/NR6Yq0DFILByKJirQJzEILAymKtAc2iR7TyYq0BKDAIrR5irQLgehetRmKtAj8L1KFyYq0BmZmZmZpirQD0K16NwmKtAFK5H4XqYq0DsUbgehZirQMP1KFyPmKtAmpmZmZmYq0BxPQrXo5irQEjhehSumKtAH4XrUbiYq0D2KFyPwpirQM3MzMzMmKtApHA9CteYq0B7FK5H4ZirQFK4HoXrmKtAKVyPwvWYq0AAAAAAAJmrQNejcD0KmatArkfhehSZq0CF61G4HpmrQFyPwvUomatAMzMzMzOZq0AK16NwPZmrQOF6FK5HmatAuB6F61GZq0CPwvUoXJmrQGZmZmZmmatAPQrXo3CZq0AUrkfhepmrQOxRuB6FmatAw/UoXI+Zq0CamZmZmZmrQHE9CtejmatASOF6FK6Zq0AfhetRuJmrQPYoXI/CmatAzczMzMyZq0CkcD0K15mrQHsUrkfhmatAUrgeheuZq0ApXI/C9ZmrQAAAAAAAmqtA16NwPQqaq0CuR+F6FJqrQIXrUbgemqtAXI/C9Siaq0AzMzMzM5qrQArXo3A9mqtA4XoUrkeaq0C4HoXrUZqrQI/C9ShcmqtAZmZmZmaaq0A9CtejcJqrQBSuR+F6mqtA7FG4HoWaq0DD9Shcj5qrQJqZmZmZmqtAcT0K16Oaq0BI4XoUrpqrQB+F61G4mqtA9ihcj8Kaq0DNzMzMzJqrQKRwPQrXmqtAexSuR+Gaq0BSuB6F65qrQClcj8L1mqtAAAAAAACbq0DXo3A9CpurQK5H4XoUm6tAhetRuB6bq0Bcj8L1KJurQDMzMzMzm6tACtejcD2bq0DhehSuR5urQLgehetRm6tAj8L1KFybq0BmZmZmZpurQD0K16Nwm6tAFK5H4Xqbq0DsUbgehZurQMP1KFyPm6tAmpmZmZmbq0BxPQrXo5urQEjhehSum6tAH4XrUbibq0D2KFyPwpurQM3MzMzMm6tApHA9Ctebq0B7FK5H4ZurQFK4HoXrm6tAKVyPwvWbq0AAAAAAAJyrQNejcD0KnKtArkfhehScq0CF61G4HpyrQFyPwvUonKtAMzMzMzOcq0AK16NwPZyrQOF6FK5HnKtAuB6F61Gcq0CPwvUoXJyrQGZmZmZmnKtAPQrXo3Ccq0AUrkfhepyrQOxRuB6FnKtAw/UoXI+cq0CamZmZmZyrQHE9CtejnKtASOF6FK6cq0AfhetRuJyrQPYoXI/CnKtAzczMzMycq0CkcD0K15yrQHsUrkfhnKtAUrgeheucq0ApXI/C9ZyrQAAAAAAAnatA16NwPQqdq0CuR+F6FJ2rQIXrUbgenatAXI/C9Sidq0AzMzMzM52rQArXo3A9natA4XoUrkedq0C4HoXrUZ2rQI/C9ShcnatAZmZmZmadq0A9CtejcJ2rQBSuR+F6natA7FG4HoWdq0DD9Shcj52rQJqZmZmZnatAcT0K16Odq0BI4XoUrp2rQB+F61G4natA9ihcj8Kdq0DNzMzMzJ2rQKRwPQrXnatAexSuR+Gdq0BSuB6F652rQClcj8L1natAAAAAAACeq0DXo3A9Cp6rQK5H4XoUnqtAhetRuB6eq0Bcj8L1KJ6rQDMzMzMznqtACtejcD2eq0DhehSuR56rQLgehetRnqtAj8L1KFyeq0BmZmZmZp6rQD0K16NwnqtAFK5H4Xqeq0DsUbgehZ6rQMP1KFyPnqtAmpmZmZmeq0BxPQrXo56rQEjhehSunqtAH4XrUbieq0D2KFyPwp6rQM3MzMzMnqtApHA9Cteeq0B7FK5H4Z6rQFK4HoXrnqtAKVyPwvWeq0AAAAAAAJ+rQNejcD0Kn6tArkfhehSfq0CF61G4Hp+rQFyPwvUon6tAMzMzMzOfq0AK16NwPZ+rQOF6FK5Hn6tAuB6F61Gfq0CPwvUoXJ+rQGZmZmZmn6tAPQrXo3Cfq0AUrkfhep+rQOxRuB6Fn6tAw/UoXI+fq0CamZmZmZ+rQHE9Ctejn6tASOF6FK6fq0AfhetRuJ+rQPYoXI/Cn6tAzczMzMyfq0CkcD0K15+rQHsUrkfhn6tAUrgeheufq0ApXI/C9Z+rQAAAAAAAoKtA16NwPQqgq0CuR+F6FKCrQIXrUbgeoKtAXI/C9Sigq0AzMzMzM6CrQArXo3A9oKtA4XoUrkegq0C4HoXrUaCrQI/C9ShcoKtAZmZmZmagq0A9CtejcKCrQBSuR+F6oKtA7FG4HoWgq0DD9Shcj6CrQJqZmZmZoKtAcT0K16Ogq0BI4XoUrqCrQB+F61G4oKtA9ihcj8Kgq0DNzMzMzKCrQKRwPQrXoKtAexSuR+Ggq0BSuB6F66CrQClcj8L1oKtAAAAAAAChq0DXo3A9CqGrQK5H4XoUoatAhetRuB6hq0Bcj8L1KKGrQDMzMzMzoatACtejcD2hq0DhehSuR6GrQLgehetRoatAj8L1KFyhq0BmZmZmZqGrQD0K16NwoatAFK5H4Xqhq0DsUbgehaGrQMP1KFyPoatAmpmZmZmhq0BxPQrXo6GrQEjhehSuoatAH4XrUbihq0D2KFyPwqGrQM3MzMzMoatApHA9Ctehq0B7FK5H4aGrQFK4HoXroatAKVyPwvWhq0AAAAAAAKKrQNejcD0KoqtArkfhehSiq0CF61G4HqKrQFyPwvUooqtAMzMzMzOiq0AK16NwPaKrQOF6FK5HoqtAuB6F61Giq0CPwvUoXKKrQGZmZmZmoqtAPQrXo3Ciq0AUrkfheqKrQOxRuB6FoqtAw/UoXI+iq0CamZmZmaKrQHE9CtejoqtASOF6FK6iq0AfhetRuKKrQPYoXI/CoqtAzczMzMyiq0CkcD0K16KrQHsUrkfhoqtAUrgeheuiq0ApXI/C9aKrQAAAAAAAo6tA16NwPQqjq0CuR+F6FKOrQIXrUbgeo6tAXI/C9Sijq0AzMzMzM6OrQArXo3A9o6tA4XoUrkejq0C4HoXrUaOrQI/C9Shco6tAZmZmZmajq0A9CtejcKOrQBSuR+F6o6tA7FG4HoWjq0DD9Shcj6OrQJqZmZmZo6tAcT0K16Ojq0BI4XoUrqOrQB+F61G4o6tA9ihcj8Kjq0DNzMzMzKOrQKRwPQrXo6tAexSuR+Gjq0BSuB6F66OrQClcj8L1o6tAAAAAAACkq0DXo3A9CqSrQK5H4XoUpKtAhetRuB6kq0Bcj8L1KKSrQDMzMzMzpKtACtejcD2kq0DhehSuR6SrQLgehetRpKtAj8L1KFykq0BmZmZmZqSrQD0K16NwpKtAFK5H4Xqkq0DsUbgehaSrQMP1KFyPpKtAmpmZmZmkq0BxPQrXo6SrQEjhehSupKtAH4XrUbikq0D2KFyPwqSrQM3MzMzMpKtApHA9Ctekq0B7FK5H4aSrQFK4HoXrpKtAKVyPwvWkq0AAAAAAAKWrQNejcD0KpatArkfhehSlq0CF61G4HqWrQFyPwvUopatAMzMzMzOlq0AK16NwPaWrQOF6FK5HpatAuB6F61Glq0CPwvUoXKWrQGZmZmZmpatAPQrXo3Clq0AUrkfheqWrQOxRuB6FpatAw/UoXI+lq0CamZmZmaWrQHE9CtejpatASOF6FK6lq0AfhetRuKWrQPYoXI/CpatAzczMzMylq0CkcD0K16WrQHsUrkfhpatAUrgeheulq0ApXI/C9aWrQAAAAAAApqtA16NwPQqmq0CuR+F6FKarQIXrUbgepqtAXI/C9Simq0AzMzMzM6arQArXo3A9pqtA4XoUrkemq0C4HoXrUaarQI/C9ShcpqtAZmZmZmamq0A9CtejcKarQBSuR+F6pqtA7FG4HoWmq0DD9Shcj6arQJqZmZmZpqtAcT0K16Omq0BI4XoUrqarQB+F61G4pqtA9ihcj8Kmq0DNzMzMzKarQKRwPQrXpqtAexSuR+Gmq0BSuB6F66arQClcj8L1pqtAAAAAAACnq0DXo3A9CqerQK5H4XoUp6tAhetRuB6nq0Bcj8L1KKerQDMzMzMzp6tACtejcD2nq0DhehSuR6erQLgehetRp6tAj8L1KFynq0BmZmZmZqerQD0K16Nwp6tAFK5H4Xqnq0DsUbgehaerQMP1KFyPp6tAmpmZmZmnq0BxPQrXo6erQEjhehSup6tAH4XrUbinq0D2KFyPwqerQM3MzMzMp6tApHA9Ctenq0B7FK5H4aerQFK4HoXrp6tAKVyPwvWnq0AAAAAAAKirQNejcD0KqKtArkfhehSoq0CF61G4HqirQFyPwvUoqKtAMzMzMzOoq0AK16NwPairQOF6FK5HqKtAuB6F61Goq0CPwvUoXKirQGZmZmZmqKtAPQrXo3Coq0AUrkfheqirQOxRuB6FqKtAw/UoXI+oq0CamZmZmairQHE9CtejqKtASOF6FK6oq0AfhetRuKirQPYoXI/CqKtAzczMzMyoq0CkcD0K16irQHsUrkfhqKtAUrgeheuoq0ApXI/C9airQAAAAAAAqatA16NwPQqpq0CuR+F6FKmrQIXrUbgeqatAXI/C9Sipq0AzMzMzM6mrQArXo3A9qatA4XoUrkepq0C4HoXrUamrQI/C9ShcqatAZmZmZmapq0A9CtejcKmrQBSuR+F6qatA7FG4HoWpq0DD9Shcj6mrQJqZmZmZqatAcT0K16Opq0BI4XoUrqmrQB+F61G4qatA9ihcj8Kpq0DNzMzMzKmrQKRwPQrXqatAexSuR+Gpq0BSuB6F66mrQClcj8L1qatAAAAAAACqq0DXo3A9CqqrQK5H4XoUqqtAhetRuB6qq0Bcj8L1KKqrQDMzMzMzqqtACtejcD2qq0DhehSuR6qrQLgehetRqqtAj8L1KFyqq0BmZmZmZqqrQD0K16NwqqtAFK5H4Xqqq0DsUbgehaqrQMP1KFyPqqtAmpmZmZmqq0BxPQrXo6qrQEjhehSuqqtAH4XrUbiqq0D2KFyPwqqrQM3MzMzMqqtApHA9Cteqq0B7FK5H4aqrQFK4HoXrqqtAKVyPwvWqq0AAAAAAAKurQNejcD0Kq6tArkfhehSrq0CF61G4HqurQFyPwvUoq6tAMzMzMzOrq0AK16NwPaurQOF6FK5Hq6tAuB6F61Grq0CPwvUoXKurQGZmZmZmq6tAPQrXo3Crq0AUrkfhequrQOxRuB6Fq6tAw/UoXI+rq0CamZmZmaurQHE9Ctejq6tASOF6FK6rq0AfhetRuKurQPYoXI/Cq6tAzczMzMyrq0CkcD0K16urQHsUrkfhq6tAUrgeheurq0ApXI/C9aurQAAAAAAArKtA16NwPQqsq0CuR+F6FKyrQIXrUbgerKtAXI/C9Sisq0AzMzMzM6yrQArXo3A9rKtA4XoUrkesq0C4HoXrUayrQI/C9ShcrKtAZmZmZmasq0A9CtejcKyrQBSuR+F6rKtA7FG4HoWsq0DD9Shcj6yrQJqZmZmZrKtAcT0K16Osq0BI4XoUrqyrQB+F61G4rKtA9ihcj8Ksq0DNzMzMzKyrQKRwPQrXrKtAexSuR+Gsq0BSuB6F66yrQClcj8L1rKtAAAAAAACtq0DXo3A9Cq2rQK5H4XoUratAhetRuB6tq0Bcj8L1KK2rQDMzMzMzratACtejcD2tq0DhehSuR62rQLgehetRratAj8L1KFytq0BmZmZmZq2rQD0K16NwratAFK5H4Xqtq0DsUbgeha2rQMP1KFyPratAmpmZmZmtq0BxPQrXo62rQEjhehSuratAH4XrUbitq0D2KFyPwq2rQM3MzMzMratApHA9Ctetq0B7FK5H4a2rQFK4HoXrratAKVyPwvWtq0AAAAAAAK6rQNejcD0KrqtArkfhehSuq0CF61G4Hq6rQFyPwvUorqtAMzMzMzOuq0AK16NwPa6rQOF6FK5HrqtAuB6F61Guq0CPwvUoXK6rQGZmZmZmrqtAPQrXo3Cuq0AUrkfheq6rQOxRuB6FrqtAw/UoXI+uq0CamZmZma6rQHE9CtejrqtASOF6FK6uq0AfhetRuK6rQPYoXI/CrqtAzczMzMyuq0CkcD0K166rQHsUrkfhrqtAUrgeheuuq0ApXI/C9a6rQAAAAAAAr6tA16NwPQqvq0CuR+F6FK+rQIXrUbger6tAXI/C9Sivq0AzMzMzM6+rQArXo3A9r6tA4XoUrkevq0C4HoXrUa+rQI/C9Shcr6tAZmZmZmavq0A9CtejcK+rQBSuR+F6r6tA7FG4HoWvq0DD9Shcj6+rQJqZmZmZr6tAcT0K16Ovq0BI4XoUrq+rQB+F61G4r6tA9ihcj8Kvq0DNzMzMzK+rQKRwPQrXr6tAexSuR+Gvq0BSuB6F66+rQClcj8L1r6tAAAAAAACwq0DXo3A9CrCrQK5H4XoUsKtAhetRuB6wq0Bcj8L1KLCrQDMzMzMzsKtACtejcD2wq0DhehSuR7CrQLgehetRsKtAj8L1KFywq0BmZmZmZrCrQD0K16NwsKtAFK5H4Xqwq0DsUbgehbCrQMP1KFyPsKtAmpmZmZmwq0BxPQrXo7CrQEjhehSusKtAH4XrUbiwq0D2KFyPwrCrQM3MzMzMsKtApHA9Ctewq0B7FK5H4bCrQFK4HoXrsKtAKVyPwvWwq0AAAAAAALGrQNejcD0KsatArkfhehSxq0CF61G4HrGrQFyPwvUosatAMzMzMzOxq0AK16NwPbGrQOF6FK5HsatAuB6F61Gxq0CPwvUoXLGrQGZmZmZmsatAPQrXo3Cxq0AUrkfherGrQOxRuB6FsatAw/UoXI+xq0CamZmZmbGrQHE9CtejsatASOF6FK6xq0AfhetRuLGrQPYoXI/CsatAzczMzMyxq0CkcD0K17GrQHsUrkfhsatAUrgeheuxq0ApXI/C9bGrQAAAAAAAsqtA16NwPQqyq0CuR+F6FLKrQIXrUbgesqtAXI/C9Siyq0AzMzMzM7KrQArXo3A9sqtA4XoUrkeyq0C4HoXrUbKrQI/C9ShcsqtAZmZmZmayq0A9CtejcLKrQBSuR+F6sqtA7FG4HoWyq0DD9Shcj7KrQJqZmZmZsqtAcT0K16Oyq0BI4XoUrrKrQB+F61G4sqtA9ihcj8Kyq0DNzMzMzLKrQKRwPQrXsqtAexSuR+Gyq0BSuB6F67KrQClcj8L1sqtAAAAAAACzq0DXo3A9CrOrQK5H4XoUs6tAhetRuB6zq0Bcj8L1KLOrQDMzMzMzs6tACtejcD2zq0DhehSuR7OrQLgehetRs6tAj8L1KFyzq0BmZmZmZrOrQD0K16Nws6tAFK5H4Xqzq0DsUbgehbOrQMP1KFyPs6tAmpmZmZmzq0BxPQrXo7OrQEjhehSus6tAH4XrUbizq0D2KFyPwrOrQM3MzMzMs6tApHA9Ctezq0B7FK5H4bOrQFK4HoXrs6tAKVyPwvWzq0AAAAAAALSrQNejcD0KtKtArkfhehS0q0CF61G4HrSrQFyPwvUotKtAMzMzMzO0q0AK16NwPbSrQOF6FK5HtKtAuB6F61G0q0CPwvUoXLSrQGZmZmZmtKtAPQrXo3C0q0AUrkfherSrQOxRuB6FtKtAw/UoXI+0q0CamZmZmbSrQHE9CtejtKtASOF6FK60q0AfhetRuLSrQPYoXI/CtKtAzczMzMy0q0CkcD0K17SrQHsUrkfhtKtAUrgeheu0q0ApXI/C9bSrQAAAAAAAtatA16NwPQq1q0CuR+F6FLWrQIXrUbgetatAXI/C9Si1q0AzMzMzM7WrQArXo3A9tatA4XoUrke1q0C4HoXrUbWrQI/C9ShctatAZmZmZma1q0A9CtejcLWrQBSuR+F6tatA7FG4HoW1q0DD9Shcj7WrQJqZmZmZtatAcT0K16O1q0BI4XoUrrWrQB+F61G4tatA9ihcj8K1q0DNzMzMzLWrQKRwPQrXtatAexSuR+G1q0BSuB6F67WrQClcj8L1tatAAAAAAAC2q0DXo3A9CrarQK5H4XoUtqtAhetRuB62q0Bcj8L1KLarQDMzMzMztqtACtejcD22q0DhehSuR7arQLgehetRtqtAj8L1KFy2q0BmZmZmZrarQD0K16NwtqtAFK5H4Xq2q0DsUbgehbarQMP1KFyPtqtAmpmZmZm2q0BxPQrXo7arQEjhehSutqtAH4XrUbi2q0D2KFyPwrarQM3MzMzMtqtApHA9Cte2q0B7FK5H4barQFK4HoXrtqtAKVyPwvW2q0AAAAAAALerQNejcD0Kt6tArkfhehS3q0CF61G4HrerQFyPwvUot6tAMzMzMzO3q0AK16NwPberQOF6FK5Ht6tAuB6F61G3q0CPwvUoXLerQGZmZmZmt6tAPQrXo3C3q0AUrkfhererQOxRuB6Ft6tAw/UoXI+3q0CamZmZmberQHE9Ctejt6tASOF6FK63q0AfhetRuLerQPYoXI/Ct6tAzczMzMy3q0CkcD0K17erQHsUrkfht6tAUrgeheu3q0ApXI/C9berQAAAAAAAuKtA16NwPQq4q0CuR+F6FLirQIXrUbgeuKtAXI/C9Si4q0AzMzMzM7irQArXo3A9uKtA4XoUrke4q0C4HoXrUbirQI/C9ShcuKtAZmZmZma4q0A9CtejcLirQBSuR+F6uKtA7FG4HoW4q0DD9Shcj7irQJqZmZmZuKtAcT0K16O4q0BI4XoUrrirQB+F61G4uKtA9ihcj8K4q0DNzMzMzLirQKRwPQrXuKtAexSuR+G4q0BSuB6F67irQClcj8L1uKtAAAAAAAC5q0DXo3A9CrmrQK5H4XoUuatAhetRuB65q0Bcj8L1KLmrQDMzMzMzuatACtejcD25q0DhehSuR7mrQLgehetRuatAj8L1KFy5q0BmZmZmZrmrQD0K16NwuatAFK5H4Xq5q0DsUbgehbmrQMP1KFyPuatAmpmZmZm5q0BxPQrXo7mrQEjhehSuuatAH4XrUbi5q0D2KFyPwrmrQM3MzMzMuatApHA9Cte5q0B7FK5H4bmrQFK4HoXruatAKVyPwvW5q0AAAAAAALqrQNejcD0KuqtArkfhehS6q0CF61G4HrqrQFyPwvUouqtAMzMzMzO6q0AK16NwPbqrQOF6FK5HuqtAuB6F61G6q0CPwvUoXLqrQGZmZmZmuqtAPQrXo3C6q0AUrkfherqrQOxRuB6FuqtAw/UoXI+6q0CamZmZmbqrQHE9CtejuqtASOF6FK66q0AfhetRuLqrQPYoXI/CuqtAzczMzMy6q0CkcD0K17qrQHsUrkfhuqtAUrgeheu6q0ApXI/C9bqrQAAAAAAAu6tA16NwPQq7q0CuR+F6FLurQIXrUbgeu6tAXI/C9Si7q0AzMzMzM7urQArXo3A9u6tA4XoUrke7q0C4HoXrUburQI/C9Shcu6tAZmZmZma7q0A9CtejcLurQBSuR+F6u6tA7FG4HoW7q0DD9Shcj7urQJqZmZmZu6tAcT0K16O7q0BI4XoUrrurQB+F61G4u6tA9ihcj8K7q0DNzMzMzLurQKRwPQrXu6tAexSuR+G7q0BSuB6F67urQClcj8L1u6tAAAAAAAC8q0DXo3A9CryrQK5H4XoUvKtAhetRuB68q0Bcj8L1KLyrQDMzMzMzvKtACtejcD28q0DhehSuR7yrQLgehetRvKtAj8L1KFy8q0BmZmZmZryrQD0K16NwvKtAFK5H4Xq8q0DsUbgehbyrQMP1KFyPvKtAmpmZmZm8q0BxPQrXo7yrQEjhehSuvKtAH4XrUbi8q0D2KFyPwryrQM3MzMzMvKtApHA9Cte8q0B7FK5H4byrQFK4HoXrvKtAKVyPwvW8q0AAAAAAAL2rQNejcD0KvatArkfhehS9q0CF61G4Hr2rQFyPwvUovatAMzMzMzO9q0AK16NwPb2rQOF6FK5HvatAuB6F61G9q0CPwvUoXL2rQGZmZmZmvatAPQrXo3C9q0AUrkfher2rQOxRuB6FvatAw/UoXI+9q0CamZmZmb2rQHE9CtejvatASOF6FK69q0AfhetRuL2rQPYoXI/CvatAzczMzMy9q0CkcD0K172rQHsUrkfhvatAUrgeheu9q0ApXI/C9b2rQAAAAAAAvqtA16NwPQq+q0CuR+F6FL6rQIXrUbgevqtAXI/C9Si+q0AzMzMzM76rQArXo3A9vqtA4XoUrke+q0C4HoXrUb6rQI/C9ShcvqtAZmZmZma+q0A9CtejcL6rQBSuR+F6vqtA7FG4HoW+q0DD9Shcj76rQJqZmZmZvqtAcT0K16O+q0BI4XoUrr6rQB+F61G4vqtA9ihcj8K+q0DNzMzMzL6rQKRwPQrXvqtAexSuR+G+q0BSuB6F676rQClcj8L1vqtAAAAAAAC/q0DXo3A9Cr+rQK5H4XoUv6tAhetRuB6/q0Bcj8L1KL+rQDMzMzMzv6tACtejcD2/q0DhehSuR7+rQLgehetRv6tAj8L1KFy/q0BmZmZmZr+rQD0K16Nwv6tAFK5H4Xq/q0DsUbgehb+rQMP1KFyPv6tAmpmZmZm/q0BxPQrXo7+rQEjhehSuv6tAH4XrUbi/q0D2KFyPwr+rQM3MzMzMv6tApHA9Cte/q0B7FK5H4b+rQFK4HoXrv6tAKVyPwvW/q0A=\",\"dtype\":\"float64\",\"order\":\"little\",\"shape\":[1000]},\"voltage (V)\":{\"__ndarray__\":\"SB999NFHzz8nnHDCCSfMP+eaa6655so/ZphhhhlmyD/GF1988cXHP8USSyyxxLI/hhZaaKGFtj+HG2644Ya7PyaXXHLJJcc/hhZaaKGFxj+GFlpooYXGP+WVV1555cU/RRVVVFFFxT9FFVVUUUXFP+WVV1555cU/hhZaaKGFxj/GF1988cXHPyaXXHLJJcc/xhdffPHFxz8ml1xyySXHP8YXX3zxxcc/xhdffPHFxz/GF1988cXHP+WVV1555cU/RRVVVFFFxT9FFVVUUUXFPyaXXHLJJcc/ZphhhhlmyD/GF1988cXHP0UVVVRRRcU/RRVVVFFFxT/nmmuuuebKPwYZZJBBBsk/hxtuuOGGyz8FFFBAAQXUP4cbbrjhhts/lFBCCSWU4D/88ccff/zhP+2yyy677OI/3XPPPffc4z+llFJKKaXkPw422GCDDeY//vbbb7/95j/GF1988cXnPz744IMPPug/BhlkkEEG6T8feuihhx7qP7/66quvvuo/r7vuuuuu6z8IHnjggQfuPyeccMIJJ+w/P/30008/7T9YXnnllVfuPyB//PHHH+8/6J9//vnn7z/ooYceeujxP9RRRx111PE/IIEEEkgg8T9cccUVV1zxPyWSSCKJJPI/7bLLLrvs8j+NM84444zzP1VUUUUVVfQ/CSWUUEIJ9T9ttdVWW231Pw422GCDDfY/6qabbrrp9j8CCCCAAAL4PzPKKKOMMvo/N9tss802+z+XWmqppZb6P0srrbTSSvs/r7vuuuuu+z+zzDLLLLP8P2edddZZZ/0/d9xxxx13/D9fe+211177P4ssssgii/w///vvv//++z8PO+ywww77P5trrrnmmvs/d9xxxx13/D+ba6655pr7P/bZZ5999vk/GmmkkUYa+T8FFFBAAQX0P2WTTTbZZPM/bbXVVltt9T/JI4888sjzP00yySSTTPI/SCGFFFJI8T9EEEEEEUTwP5hffvnll+8/mF9++eWX7z/43nvvvffuP/jggw8++PA/eeONN9548z/CBhtssMH2PzbWWGONNfY/7rfffvvt9z/KKKOMMsr4P+eaa6655vo/hxtuuOGG+z8wvvjiiy/+PxxuuOGGG/4/+N577733/j+EDz744IP/PwgggAACCABARBBBBBFEAECAAAIIIIAAQLzwwgsvvABAIIEEEkggAUDaaKONNtoAQMUSSyyxxAJAvPDCCy+8AECEEUYYYYQBQI455phjjgFAyimnnHLKAUAlkkgiiSQCQEMKKaSQQgJAYYIJJphgAkCJIooooogCQM8666yzzgJAKaOMMsooA0BHG2200UYDQI0zzjjjjANAQQQRRBBBBECDCy644IIDQFtrrbXWWgNAPfPMM888A0BVVFFFFVUEQN1zzz333ANAH3vsscceA0C76qqrrroCQGuqqaaaagJA/PHHH3/8AUC22WabbbYBQD755JNPPgFAjjnmmGOOAUBcb7311lv/P8YYY4wxxgBAiiiiiCKKAEDA//7777//P7zuuuuuu/4/H3rooYce+j9baqmlllr6PzvssMMOO/w/s8wyyyyz/D+jjTbaaKP9PzC++OKLL/4/CCCAAAIIAEBssMEGG2wAQGaZZZZZZgFATTLJJJNMAkD88ccff/wBQDniiCOOOAJAk0oqqaSSAkDPOuuss84CQDPLLLPMMgNAgwsuuOCCA0Ddc88999wDQEEEEUQQQQRAffTRRx99BEDrrLPOOusEQDvttNNOOwVAvfXWW2+9BUAihhhiiCEGQHLGGWeccQZA4H777bffBkBYX3311VcHQKiffvrppwdADDDAAAMMCECOOOKII44IQKywwgorrAhA6KCDDjroCEAkkUQSSSQJQGCBBRZYYAlAkkkmmWSSCUDssccee+wJQDPKKKOMMgpAjTLKKKOMCkDdcsstt9wKQDfbbLPNNgtAr7vuuuuuC0AnnHDCCScMQGOMMcYYYwxAY4wxxhhjDEBjjDHGGGMMQDvssMMOOwxAJ5xwwgknDEDrq6+++uoLQOGDDz744AtAhxtuuOGGC0APO+ywww4LQNNKK6200gpADzvssMMOC0AGGWSQQQYJQI444ogjjghAcMABBxxwCECYYIIJJpgIQJhgggkmmAhA6KCDDjroCEApoogiiigKQFVTTTXVVAtAc8stt9xyC0AxxBBDDDEMQL300ksvvQxAF1100UUXDUBxxRVXXHENQLfddttttw1AEkYYYYQRDkBEDjnkkEMOQNA+++yzzw5AKqeccsopD0Cihx566KEPQAMMMMAAAxBARBBBBBFEEECZZJJJJpkQQOmkk0466RBAOeWUU045EUCOOeaYY44RQMUVV1xxxRFA/PHHH3/8EUA0zjjjjDMSQE0yySSTTBJAcL755ptvEkCONtpoo40SQKeaaqqpphJAu+qqq666EkDUTjvttNMSQOOKK6644hJA/O677777EkAaZ5xxxhkTQD3zzDPPPBNAZZNNNtlkE0CSRx555JETQLrnnnvuuRNA55tvvvnmE0AAAAAAAAAUQAAAAAAAABRAAAAAAAAAFEAAAAAAAAAUQAAAAAAAABRAAAAAAAAAFEAAAAAAAAAUQAAAAAAAABRAAAAAAAAAFEAAAAAAAAAUQAAAAAAAABRAAAAAAAAAFEAAAAAAAAAUQAAAAAAAABRAAAAAAAAAFEAAAAAAAAAUQAAAAAAAABRAAAAAAAAAFEAAAAAAAAAUQAAAAAAAABRAAAAAAAAAFEAAAAAAAAAUQAAAAAAAABRAAAAAAAAAFEAAAAAAAAAUQAAAAAAAABRAAAAAAAAAFEAAAAAAAAAUQLC//vrrrxNAW2uttdZaE0D87rvvvvsSQMomm2yyyRJAhA466KCDEkBDCimkkEISQAIGGGCAARJAu+222267EUB66aWXXnoRQEMNNdRQQxFADDHEEEMMEUDQQAMNNNAQQMsss8wyyxBAyyyzzDLLEEDLLLPMMssQQNVUU0011RBA0EADDTTQEEC33HLLLbcQQMsss8wyyxBAvPDCCy+8EEDLLLPMMssQQKigggoqqBBAssgiiyyyEEDQQAMNNNAQQKigggoqqBBAssgiiyyyEECyyCKLLLIQQKigggoqqBBAssgiiyyyEECeeOKJJ54QQKOMMsoooxBAo4wyyiijEEDBBBNMMMEQQNBAAw000BBAwQQTTDDBEEDVVFNNNdUQQN9888033xBA6aSTTjrpEEDppJNOOukQQNpoo4022hBA1VRTTTXVEEC88MILL7wQQIAAAggggBBATjjhhBNOEECihx566KEPQCB//PHHHw9AAgcccMABD0DaZpttttkOQJ522mmnnQ5ARA455JBDDkD99ddff/0NQIUVVlhhhQ1A77zzzjvvDEBttNFGG20MQP/777///gtAm2uuueaaC0BLK6200koLQPvqq6+++gpAjTLKKKOMCkAzyiijjDIKQMQRRxxxxAlAdNFFF110CUDssccee+wJQJJJJplkkglAkkkmmWSSCUDEEUccccQJQFFCCSWUUApA55prrrnmCkBVU0011VQLQNdbb7311gtAY4wxxhhjDED55JNPPvkMQOmll1566Q1AbK655pprDkCUTjrppJMOQLzuuuuuuw5AssYaa6yxDkC87rrrrrsOQMYWW2yxxQ5AqJ566qmnDkBihhlmmGEOQDC++OKLLw5ArbXWWmutDUA11VRTTTUNQCuttNJKKw1A+eSTTz75DED55JNPPvkMQMccc8wxxwxA22yzzTbbDEC99NJLL70MQBNMMMEEEwxAMcQQQwwxDEDvvPPOO+8MQJlllllmmQ1AbK655pprDkAqp5xyyikPQMD//vvvvw9AJphgggkmEEBddNFFF10QQIAAAggggBBAhRRSSCGFEECFFFJIIYUQQGywwQYbbBBAZ5xxxhlnEEBYYIEFFlgQQBdccMEFFxBAPvfcc889D0B21llnnXUOQK211lprrQ1AK6200korDUC99NJLL70MQDvssMMOOwxApZNOOumkC0APO+ywww4LQFFCCSWUUApAOOGEE044CUBIIIEEEkgIQHbXXXfddQdAWF999dVXB0Aml1xyySUHQMwuu+yyywZANtZYY401BkDHHXfccccFQE899dRTTwVAMcUUU0wxBUDrrLPOOusEQKWUUkoppQRAc8wxxxxzBEAZZJBBBhkEQFFDDTXUUANAk0oqqaSSAkDyySeffPIBQI455phjjgFANNFEE000AUAqqaSSSioBQO6444477gBAiiiiiCKKAEAmmGCCCSYAQJhffvnll/8/gP75559//j977bXXXnv9PwMNNNBAA/0/n3zyySef/D/DCy+88ML7PzfbbLPNNvs/++qrr776+j/nmmuuueb6P+eaa6655vo/DzvssMMO+z8PO+ywww77Pw877LDDDvs/DzvssMMO+z8jiyyyyCL7P1977bXXXvs/m2uuueaa+z9PPPHEE0/8P++888477/w/K6200kor/T/ffffdd9/9P7fddtttt/0/e+211157/T9nnXXWWWf9P99999133/0/CB544IEH/j9srrnmmmv+P4D++eeff/4/0D777LPP/j8ML7zwwgv/P1xvvfXWW/8/mF9++eWX/z866KCDDjoAQGywwQYbbABAnnjiiSeeAEC88MILL7wAQNpoo4022gBADDHEEEMMAUAggQQSSCABQDTRRBNNNAFAXHHFFVdcAUBwwQUXXHABQKyxxhprrAFA1FFHHXXUAUD88ccff/wBQCWSSCKJJAJAV1pppZVWAkCJIooooogCQLHCCiussAJA7bLLLrvsAkAfe+yxxx4DQD3zzDPPPANAZZNNNtlkA0CXW2655ZYDQLXTTjvttANADzzwwAMPBECvvPLKK68EQGONNdZYYwVA+eWXX375BUBKJplkkkkGQHLGGWeccQZAhhZaaKGFBkCQPvroo48GQJpmmmmmmQZAmmaaaaaZBkCaZpppppkGQJpmmmmmmQZAmmaaaaaZBkC43nrrrbcGQJpmmmmmmQZAhhZaaKGFBkByxhlnnHEGQKSOOuqoowZABA444IADBkDvvffee+8FQDvttNNOOwVAE0000UQTBUB33XXXXXcFQIsttthiiwVAr7zyyiuvBEBttdVWW20FQNdcc8011wRAO+200047BUCfffbZZ58FQJpmmmmmmQZA6qabbrrpBkA655xzzjkHQIonnnjiiQdA+N9///33B0BSSCGFFFIIQKKIIooooghA/PDDDz/8CEBCCSWUUEIJQH755ZdffglAkkkmmWSSCUDssccee+wJQB966KGHHgpAPfLII488CkBbaqmllloKQFFCCSWUUApAgwoqqKCCCkC10korrbQKQAUTTDDBBAtAaaONNtpoC0DDCy+88MILQBNMMMEEEwxATzzxxBNPDECffPLJJ58MQNFEE0000QxAK6200korDUCFFVZYYYUNQNVVV1111Q1AML744osvDkCA/vnnn38OQLLGGmussQ5A2mabbbbZDkAML7zwwgsPQDTPPPPMMw9AUkcdddRRD0CEDz744IMPQLbXXnvttQ9A1E8//fTTD0Dyxx9//PEPQA000EADDRBAK6ywwgorEEBEEEEEEUQQQF100UUXXRBAe+yxxx57EEBJJJFEEkkQQGywwQYbbBBAK6ywwgorEEArrLDCCisQQF100UUXXRBATjjhhBNOEEAhhBBCCCEQQMD//vvvvw9AKqeccsopD0CyxhprrLEOQOmll1566Q1A1VVXXXXVDUCZZZZZZpkNQGedddZZZw1AP/30008/DUANNdRQQw0NQNFEE0000QxAlVRSSSWVDEBjjDHGGGMMQDHEEEMMMQxA4YMPPvjgC0CRQw455JALQKWTTjrppAtAN9tss802C0ChggoqqKAKQG+66aabbgpAPfLII488CkDEEUccccQJQGCBBRZYYAlA8sgjjzzyCEBwwAEHHHAIQAIIIIAAAghAML/88ssvB0ASRxxxxBEHQNZWW2211QZAuN566623BkCGFlpooYUGQIYWWmihhQZAQP74448/BkAsrrjiiisGQOWVV1555QVAn3322WefBUCVVVZZZZUFQG211VZbbQVAE0000UQTBUDXXHPNNdcEQJtssskmmwRAN9xwww03BEBVVFFFFVUEQA888MADDwRAoYMOOuigA0DZYostttgCQJ1yyimnnAJAOeKII444AkD88ccff/wBQLbZZptttgFAUkkllVRSAUAqqaSSSioBQBZZZJFFFgFAAgkkkEACAUDuuOOOO+4AQLzwwgsvvABAlFBCCSWUAECAAAIIIIAAQGKIIYYYYgBATjjhhBNOAEA66KCDDjoAQBxwwAEHHABA6J9//vnn/z+EDz744IP/PzTPPPPMM/8/+N577733/j+onnrqqaf+PzC++OKLL/4/33333Xff/T9nnXXWWWf9Pz/99NNPP/0/P/30008//T9nnXXWWWf9P9tss8022/w/d9xxxx13/D8nnHDCCSf8P+urr7766vs/hxtuuOGG+z8jiyyyyCL7P+eaa6655vo/l1pqqaWW+j8zyiijjDL6P5daaqmllvo/9tlnn332+T/22Wefffb5P7rppptuuvk/BhlkkEEG+T966KGHHnr4P1JIIYUUUvg/2mefffbZ9z8++OCDDz74P3bXXXfddfc/ffTRRx999D+VVVZZZZX1P6622mqrrfY/0UUXXXTR9T8ZZJBBBhn0PxJHHHHEEfc/EkccccQR9z910kknnXTyPymjjDLKKPM/iSKKKKKI8j8RQgghhBDyP+SQQw455PA/OeKII4448j+YYYYZZpjxP1xxxRVXXPE/0EADDTTQ8D8IIIAAAgjwPyB//PHHH+8/CB544IEH7j/vvPPOO+/sPyeccMIJJ+w/N9tss8026z+mmWaaaabpPz744IMPPug//vbbb7/95j+VVVZZZZXlPy200EILLeQ/7bLLLrvs4j9cccUVV1zhPxxwwAEHHOA/pplmmmmm2T9211133XXXPzbWWGONNdY/9dRTTz311D9VVFFFFVXUP7XTTjvttNM/FVNMMcUU0z910kknnXTSP4QRRhhhhNE/6J9//vnnzz/nmmuuuebKP7XTTjvttNM/pplmmmmm2T/WVltttdXWP/XUU0899dQ/SB999NFHzz/on3/++efPP+iff/75588/55prrrnmyj8FFFBAAQWUPwUUUEABBZQ/BRRQQAEFlD8IHnjggQeOPwAAAAAAAAAABRRQQAEFlD8GGWSQQQaZPwUUUEABBXQ/CB544IEHnj/11FNPPfXUPxVTTDHFFNM/BRRQQAEFlD8IHnjggQeOP4cbbrjhhqs/RRVVVFFF1T+EEUYYYYSxPyaXXHLJJcc/tthiiy222D/FEkssscTCP+eaa6655to/BRRQQAEFlD8IHnjggQeuP4cbbrjhhqs/xRJLLLHEsj+HG2644Ya7P2WTTTbZZMM/xhdffPHFxz+HG2644YbLP0gfffTRR88/1FFHHXXU0T/FEkssscTSP1VUUUUVVdQ/5ZVXXnnl1T8WWGCBBRbYP1ZZZZVVVtk/9tlnn3322T/22WefffbZP8ccc8wxx9w/xxxzzDHH3D+onnrqqafeP5hffvnll98/5JBDDjnk4D+sscYaa6zhPyWSSCKJJOI/xRJLLLHE4j9lk0022WTjPy200EILLeQ/zTTTTDPN5D+VVVZZZZXlP1522WWXXeY/rrbaaqut5j9ON910003nP0433XTTTec/nnfeeeed5z8WWGCBBRboP2aYYYYZZug/3njjjTfe6D+mmWaaaabpP7/66quvvuo/r7vuuuuu6z+ffPLJJ5/sPxdddNFFF+0/jz322GOP7T8IHnjggQfuP1heeeWVV+4/6J9//vnn7z/QQAMNNNDwP/jggw8++PA/CCCAAAII8D8ccMABBxzwP2ywwQYbbPA/vPDCCy+88D800UQTTTTxP5hhhhlmmPE/6KGHHnro8T9hggkmmGDyP8USSyyxxPI/KaOMMsoo8z9lk0022WTzP91zzz333PM/QQQRRBBB9D+llFJKKaX0P/XUU0899fQ/McUUU0wx9T+BBRZYYIH1P9FFF1100fU/NtZYY4019j9yxhlnnHH2P/7222+//fY/TjfddNNN9z+yxx577LH3P1JIIYUUUvg/3njjjTfe+D9WWWWVVVb5P6aZZppppvk/CyqooIIK+j9baqmlllr6P4MKKqiggvo/00orrbTS+j8PO+ywww77P3PLLbfccvs/r7vuuuuu+z8TTDDBBBP8P4ssssgii/w/K6200kor/T/zzTfffPP9P/jee++99/4/MMAAAwwwAECooIIKKqgAQEghhRRSSAFAmGGGGWaYAUDeeeedd94BQAcaaKCBBgJA3nnnnXfeAUDyySeffPIBQMopp5xyygFA1FFHHXXUAUDeeeedd94BQMopp5xyygFAPvnkk08+AUAMMcQQQwwBQOSQQw455ABAqKCCCiqoAEA66KCDDjoAQMD//vvvv/8/ML744osv/j8DDTTQQAP9PzvssMMOO/w/c8stt9xy+z8322yzzTb7PyOLLLLIIvs/I4ssssgi+z8322yzzTb7P1977bXXXvs/DzvssMMO+z9LK6200kr7P5trrrnmmvs/O+ywww47/D/HHHPMMcf8PyuttNJKK/0/yy233HLL/T+UTjrppJP+P6yvvvrqq/8/TjjhhBNOAECooIIKKqgAQFxxxRVXXAFAjjnmmGOOAUCsscYaa6wBQPzxxx9//AFAG2qooYYaAkAlkkgiiSQCQC+66KKLLgJAOeKII444AkBXWmmllVYCQH/66aeffgJAp5pqqqmmAkDFEkssscQCQOOKK6644gJA99prr732AkALK6ywwgoDQEcbbbTRRgNAb7vttttuA0CXW2655ZYDQOebb7755gNADzzwwAMPBEBBBBFEEEEEQGmkkUYaaQRApZRSSimlBED//PPPP/8EQEUVVVRRRQVAiy222GKLBUCzzTbbbLMFQPnll19++QVALK644oorBkCQPvroo48GQPTOO++88wZAJpdccsklB0BEDz300EMHQEQPPfTQQwdARA899NBDB0Awv/zyyy8HQDC//PLLLwdARA899NBDB0CkjjrqqKMGQJ999tlnnwVA11xzzTXXBEBLLLHEEksEQC200EILLQRALbTQQgstBEAttNBCCy0EQC200EILLQRALbTQQgstBEAttNBCCy0EQDfccMMNNwRASyyxxBJLBEBppJFGGmkEQHPMMccccwRAffTRRx99BEC55JJLLrkEQAkllFBCCQVARRVVVFFFBUCppZZaaqkFQPnll19++QVASiaZZJJJBkCaZpppppkGQMwuu+yyywZAEkccccQRB0A655xzzjkHQJRPPvnkkwdA0D///PPPB0AggAACCCAIQD744IMPPghAZphhhhlmCECOOOKII44IQMooo4wyyghAJJFEEkkkCUCSSSaZZJIJQAsqqKCCCgpAoYIKKqigCkAFE0wwwQQLQEEDDTTQQAtAhxtuuOGGC0Clk0466aQLQK+77rrrrgtAueOOO+64C0DDCy+88MILQMMLL7zwwgtA4YMPPvjgC0D1008//fQLQBNMMMEEEwxAO+ywww47DEBFFFFEEUUMQGOMMcYYYwxAY4wxxhhjDEBjjDHGGGMMQGOMMcYYYwxAWWSRRRZZDEBZZJFFFlkMQLPMMsssswxAY4wxxhhjDEATTDDBBBMMQM0zzzzzzAtAc8stt9xyC0APO+ywww4LQKGCCiqooApAM8ooo4wyCkDiiSeeeOIJQJJJJplkkglAVllllVVWCUDyyCOPPPIIQJhgggkmmAhAUkghhRRSCEBSSCGFFFIIQAwwwAADDAhADDDAAAMMCEACCCCAAAIIQEgggQQSSAhAUkghhRRSCECEEEIIIYQIQEgggQQSSAhAoogiiiiiCEAaaaSRRhoJQH755ZdffglA7LHHHnvsCUBlkkkmmWQKQL/66quvvgpAaaONNtpoC0Cvu+66664LQM0zzzzzzAtA11tvvfXWC0D1008//fQLQB100EEHHQxAWWSRRRZZDECppJJKKqkMQNtss8022wxADTXUUEMNDUBJJZVUUkkNQHvttddeew1AwQUXXHDBDUAmllhiiSUOQLLGGmussQ5AFldcccUVD0Csr7766qsPQAgggAACCBBANdRQQw01EEBYYIEFFlgQQHvsscceexBAjzzyyCOPEECUUEIJJZQQQJRQQgkllBBAmWSSSSaZEEDGGGOMMcYQQK200korrRBA1VRTTTXVEEDBBBNMMMEQQLfccssttxBAwQQTTDDBEEDBBBNMMMEQQMEEE0wwwRBAxhhjjDHGEEDBBBNMMMEQQOSQQw455BBA5JBDDjnkEEDkkEMOOeQQQOSQQw455BBA88wzzzzzEEDaaKONNtoQQOSQQw455BBA33zzzTffEEDVVFNNNdUQQOSQQw455BBA1VRTTTXVEEDaaKONNtoQQNVUU0011RBA88wzzzzzEEAMMcQQQwwRQCqppJJKKhFAUkkllVRSEUB//fXXX38RQIQRRhhhhBFAookmmmiiEUA=\",\"dtype\":\"float64\",\"order\":\"little\",\"shape\":[1000]}},\"selected\":{\"id\":\"1047\"},\"selection_policy\":{\"id\":\"1046\"}},\"id\":\"1033\",\"type\":\"ColumnDataSource\"},{\"attributes\":{\"bottom_units\":\"screen\",\"fill_alpha\":0.5,\"fill_color\":\"lightgrey\",\"left_units\":\"screen\",\"level\":\"overlay\",\"line_alpha\":1.0,\"line_color\":\"black\",\"line_dash\":[4,4],\"line_width\":2,\"right_units\":\"screen\",\"top_units\":\"screen\"},\"id\":\"1025\",\"type\":\"BoxAnnotation\"},{\"attributes\":{},\"id\":\"1009\",\"type\":\"LinearScale\"},{\"attributes\":{},\"id\":\"1016\",\"type\":\"BasicTicker\"},{\"attributes\":{},\"id\":\"1019\",\"type\":\"PanTool\"}],\"root_ids\":[\"1002\"]},\"title\":\"Bokeh Application\",\"version\":\"2.2.1\"}};\n", " var render_items = [{\"docid\":\"106cabbe-64a8-4458-ac29-e0855767304d\",\"root_ids\":[\"1002\"],\"roots\":{\"1002\":\"1a8cc927-e7c7-4023-be18-b5b8a686e30e\"}}];\n", " root.Bokeh.embed.embed_items_notebook(docs_json, render_items);\n", "\n", " }\n", " if (root.Bokeh !== undefined) {\n", " embed_document(root);\n", " } else {\n", " var attempts = 0;\n", " var timer = setInterval(function(root) {\n", " if (root.Bokeh !== undefined) {\n", " clearInterval(timer);\n", " embed_document(root);\n", " } else {\n", " attempts++;\n", " if (attempts > 100) {\n", " clearInterval(timer);\n", " console.log(\"Bokeh: ERROR: Unable to run BokehJS code because BokehJS library is missing\");\n", " }\n", " }\n", " }, 10, root)\n", " }\n", "})(window);" ], "application/vnd.bokehjs_exec.v0+json": "" }, "metadata": { "application/vnd.bokehjs_exec.v0+json": { "id": "1002" } }, "output_type": "display_data" } ], "source": [ "df['time (sec)'] = df['time (ms)'] / 1000\n", "\n", "p = bokeh.plotting.figure(\n", " x_axis_label='time (s)',\n", " y_axis_label='voltage (V)',\n", " frame_height=175,\n", " frame_width=500,\n", " x_range=[df['time (sec)'].min(), df['time (sec)'].max()],\n", ")\n", "p.line(source=df, x='time (sec)', y='voltage (V)')\n", "\n", "bokeh.io.show(p)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "*You are not required to submit this exercise.*\n", "\n", "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Comparison of timing\n", "\n", "We saw that when we acquired data on request using a call to `time.sleep()` to wait for the request, we got ill-timed data. Let's generate those data again, and compare to the timing we got by streaming data." ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [], "source": [ "time_ms = []\n", "voltage = []\n", "\n", "def request_single_voltage(arduino):\n", " \"\"\"Ask Arduino for a single data point\"\"\"\n", " # Ask Arduino for data\n", " arduino.write(bytes([VOLTAGE_REQUEST]))\n", "\n", " # Read in the data\n", " raw = arduino.read_until()\n", "\n", " # Parse and return\n", " return parse_raw(raw)\n", "\n", "\n", "for i in range(1000):\n", " # Request and append\n", " t, V = request_single_voltage(arduino)\n", " time_ms.append(t)\n", " voltage.append(V)\n", "\n", " # Wait 20 ms\n", " time.sleep(0.02)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now, we can compute the differences in the time intervals and make a plot of how many samples for each inter-sample time we got." ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", "\n", "\n", "\n", "\n", "\n", "
\n" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "application/javascript": [ "(function(root) {\n", " function embed_document(root) {\n", " \n", " var docs_json = {\"03c149a7-f423-4527-b492-93054b39b585\":{\"roots\":{\"references\":[{\"attributes\":{\"below\":[{\"id\":\"1102\"}],\"center\":[{\"id\":\"1105\"},{\"id\":\"1109\"},{\"id\":\"1138\"}],\"frame_height\":175,\"frame_width\":300,\"left\":[{\"id\":\"1106\"}],\"renderers\":[{\"id\":\"1127\"},{\"id\":\"1143\"}],\"title\":{\"id\":\"1129\"},\"toolbar\":{\"id\":\"1117\"},\"x_range\":{\"id\":\"1094\"},\"x_scale\":{\"id\":\"1098\"},\"y_range\":{\"id\":\"1096\"},\"y_scale\":{\"id\":\"1100\"}},\"id\":\"1093\",\"subtype\":\"Figure\",\"type\":\"Plot\"},{\"attributes\":{},\"id\":\"1111\",\"type\":\"WheelZoomTool\"},{\"attributes\":{\"overlay\":{\"id\":\"1116\"}},\"id\":\"1112\",\"type\":\"BoxZoomTool\"},{\"attributes\":{\"active_drag\":\"auto\",\"active_inspect\":\"auto\",\"active_multi\":null,\"active_scroll\":\"auto\",\"active_tap\":\"auto\",\"tools\":[{\"id\":\"1110\"},{\"id\":\"1111\"},{\"id\":\"1112\"},{\"id\":\"1113\"},{\"id\":\"1114\"},{\"id\":\"1115\"}]},\"id\":\"1117\",\"type\":\"Toolbar\"},{\"attributes\":{},\"id\":\"1115\",\"type\":\"HelpTool\"},{\"attributes\":{\"label\":{\"value\":\"stream\"},\"renderers\":[{\"id\":\"1127\"}]},\"id\":\"1139\",\"type\":\"LegendItem\"},{\"attributes\":{\"axis\":{\"id\":\"1102\"},\"ticker\":null},\"id\":\"1105\",\"type\":\"Grid\"},{\"attributes\":{},\"id\":\"1113\",\"type\":\"SaveTool\"},{\"attributes\":{\"items\":[{\"id\":\"1139\"},{\"id\":\"1156\"}]},\"id\":\"1138\",\"type\":\"Legend\"},{\"attributes\":{},\"id\":\"1114\",\"type\":\"ResetTool\"},{\"attributes\":{\"source\":{\"id\":\"1124\"}},\"id\":\"1128\",\"type\":\"CDSView\"},{\"attributes\":{\"fill_color\":{\"value\":\"orange\"},\"line_color\":{\"value\":\"orange\"},\"x\":{\"field\":\"x\"},\"y\":{\"field\":\"y\"}},\"id\":\"1141\",\"type\":\"Circle\"},{\"attributes\":{\"data_source\":{\"id\":\"1124\"},\"glyph\":{\"id\":\"1125\"},\"hover_glyph\":null,\"muted_glyph\":null,\"nonselection_glyph\":{\"id\":\"1126\"},\"selection_glyph\":null,\"view\":{\"id\":\"1128\"}},\"id\":\"1127\",\"type\":\"GlyphRenderer\"},{\"attributes\":{\"fill_color\":{\"value\":\"#1f77b4\"},\"line_color\":{\"value\":\"#1f77b4\"},\"x\":{\"field\":\"x\"},\"y\":{\"field\":\"y\"}},\"id\":\"1125\",\"type\":\"Circle\"},{\"attributes\":{\"axis\":{\"id\":\"1106\"},\"dimension\":1,\"ticker\":null},\"id\":\"1109\",\"type\":\"Grid\"},{\"attributes\":{\"data\":{\"x\":{\"__ndarray__\":\"AAAAAAAANEAAAAAAAAA1QA==\",\"dtype\":\"float64\",\"order\":\"little\",\"shape\":[2]},\"y\":[998,1]},\"selected\":{\"id\":\"1137\"},\"selection_policy\":{\"id\":\"1136\"}},\"id\":\"1124\",\"type\":\"ColumnDataSource\"},{\"attributes\":{\"label\":{\"value\":\"on demand\"},\"renderers\":[{\"id\":\"1143\"}]},\"id\":\"1156\",\"type\":\"LegendItem\"},{\"attributes\":{\"source\":{\"id\":\"1140\"}},\"id\":\"1144\",\"type\":\"CDSView\"},{\"attributes\":{},\"id\":\"1131\",\"type\":\"BasicTickFormatter\"},{\"attributes\":{},\"id\":\"1107\",\"type\":\"BasicTicker\"},{\"attributes\":{\"text\":\"\"},\"id\":\"1129\",\"type\":\"Title\"},{\"attributes\":{\"data\":{\"x\":[22,23,24,25,26,27,28,29,30,31,32],\"y\":[4,38,74,140,159,152,151,149,87,41,4]},\"selected\":{\"id\":\"1155\"},\"selection_policy\":{\"id\":\"1154\"}},\"id\":\"1140\",\"type\":\"ColumnDataSource\"},{\"attributes\":{},\"id\":\"1133\",\"type\":\"BasicTickFormatter\"},{\"attributes\":{\"data_source\":{\"id\":\"1140\"},\"glyph\":{\"id\":\"1141\"},\"hover_glyph\":null,\"muted_glyph\":null,\"nonselection_glyph\":{\"id\":\"1142\"},\"selection_glyph\":null,\"view\":{\"id\":\"1144\"}},\"id\":\"1143\",\"type\":\"GlyphRenderer\"},{\"attributes\":{},\"id\":\"1110\",\"type\":\"PanTool\"},{\"attributes\":{},\"id\":\"1098\",\"type\":\"LinearScale\"},{\"attributes\":{\"axis_label\":\"number of samples\",\"formatter\":{\"id\":\"1131\"},\"ticker\":{\"id\":\"1107\"}},\"id\":\"1106\",\"type\":\"LinearAxis\"},{\"attributes\":{\"fill_alpha\":{\"value\":0.1},\"fill_color\":{\"value\":\"#1f77b4\"},\"line_alpha\":{\"value\":0.1},\"line_color\":{\"value\":\"#1f77b4\"},\"x\":{\"field\":\"x\"},\"y\":{\"field\":\"y\"}},\"id\":\"1126\",\"type\":\"Circle\"},{\"attributes\":{\"bottom_units\":\"screen\",\"fill_alpha\":0.5,\"fill_color\":\"lightgrey\",\"left_units\":\"screen\",\"level\":\"overlay\",\"line_alpha\":1.0,\"line_color\":\"black\",\"line_dash\":[4,4],\"line_width\":2,\"right_units\":\"screen\",\"top_units\":\"screen\"},\"id\":\"1116\",\"type\":\"BoxAnnotation\"},{\"attributes\":{},\"id\":\"1154\",\"type\":\"UnionRenderers\"},{\"attributes\":{},\"id\":\"1155\",\"type\":\"Selection\"},{\"attributes\":{\"fill_alpha\":{\"value\":0.1},\"fill_color\":{\"value\":\"orange\"},\"line_alpha\":{\"value\":0.1},\"line_color\":{\"value\":\"orange\"},\"x\":{\"field\":\"x\"},\"y\":{\"field\":\"y\"}},\"id\":\"1142\",\"type\":\"Circle\"},{\"attributes\":{\"axis_label\":\"\\u0394t (ms)\",\"formatter\":{\"id\":\"1133\"},\"ticker\":{\"id\":\"1103\"}},\"id\":\"1102\",\"type\":\"LinearAxis\"},{\"attributes\":{},\"id\":\"1136\",\"type\":\"UnionRenderers\"},{\"attributes\":{},\"id\":\"1096\",\"type\":\"DataRange1d\"},{\"attributes\":{},\"id\":\"1137\",\"type\":\"Selection\"},{\"attributes\":{},\"id\":\"1100\",\"type\":\"LinearScale\"},{\"attributes\":{},\"id\":\"1094\",\"type\":\"DataRange1d\"},{\"attributes\":{},\"id\":\"1103\",\"type\":\"BasicTicker\"}],\"root_ids\":[\"1093\"]},\"title\":\"Bokeh Application\",\"version\":\"2.2.1\"}};\n", " var render_items = [{\"docid\":\"03c149a7-f423-4527-b492-93054b39b585\",\"root_ids\":[\"1093\"],\"roots\":{\"1093\":\"e18320ca-6837-464d-8f7f-c38ed8d8ea54\"}}];\n", " root.Bokeh.embed.embed_items_notebook(docs_json, render_items);\n", "\n", " }\n", " if (root.Bokeh !== undefined) {\n", " embed_document(root);\n", " } else {\n", " var attempts = 0;\n", " var timer = setInterval(function(root) {\n", " if (root.Bokeh !== undefined) {\n", " clearInterval(timer);\n", " embed_document(root);\n", " } else {\n", " attempts++;\n", " if (attempts > 100) {\n", " clearInterval(timer);\n", " console.log(\"Bokeh: ERROR: Unable to run BokehJS code because BokehJS library is missing\");\n", " }\n", " }\n", " }, 10, root)\n", " }\n", "})(window);" ], "application/vnd.bokehjs_exec.v0+json": "" }, "metadata": { "application/vnd.bokehjs_exec.v0+json": { "id": "1093" } }, "output_type": "display_data" } ], "source": [ "dt_stream = np.diff(df['time (ms)'])\n", "dt_on_demand = np.diff(time_ms)\n", "\n", "\n", "dt_stream, counts_stream = np.unique(dt_stream, return_counts=True)\n", "dt_on_demand, counts_on_demand = np.unique(dt_on_demand, return_counts=True)\n", "\n", "p = bokeh.plotting.figure(\n", " x_axis_label='Δt (ms)',\n", " y_axis_label='number of samples',\n", " frame_height=175,\n", " frame_width=300,\n", ")\n", "p.circle(dt_stream, counts_stream, legend_label='stream')\n", "p.circle(dt_on_demand, counts_on_demand, legend_label='on demand', color='orange')\n", "\n", "bokeh.io.show(p)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Clearly, streaming has much better performance!" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [], "source": [ "arduino.close()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Computing environment" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "CPython 3.8.5\n", "IPython 7.18.1\n", "\n", "numpy 1.19.1\n", "pandas 1.1.1\n", "serial 3.4\n", "bokeh 2.2.1\n", "jupyterlab 2.2.6\n" ] } ], "source": [ "%load_ext watermark\n", "%watermark -v -p numpy,pandas,serial,bokeh,jupyterlab" ] } ], "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.8.8" } }, "nbformat": 4, "nbformat_minor": 4 }