diff --git a/README.md b/README.md index 7123de6..49727aa 100644 --- a/README.md +++ b/README.md @@ -8,19 +8,19 @@ This software allows GUI (Graphical User Interface) driven live collection, plotting and analysis of digitized data inside a Jupyter notebook. The package was initially developed to provide an inexpensive laboratory system for teaching based on -the Raspberry Pi. __However, it now also works on other hardware__. +the Raspberry Pi. **However, it now works on other hardware**. Presently the working combinations are: -__on Raspberry Pis__ +**on Raspberry Pis** * Adafruit compliant ADS1115 boards ([example](https://www.amazon.com/KNACRO-4-Channel-Raspberry-ADS1115-Channel/dp/B07149WH7P), also available from other vendors); * The [π-Plates DAQC2 plate](https://pi-plates.com/daqc2r1/). -__on Macs (and probably Windows)__ +**on Macs and Windows** * [Vernier](https://www.vernier.com) LabQuest USB A-to-Ds. -__demo mode on anything Jupyter runs on__ +**demo mode on anything Jupyter runs on** * A demo mode will run on any computer with a Jupyter notebook install and Python 3.6+. You can try the demo mode without installing on your own computer by launching an instance on the MyBinder servers: @@ -37,14 +37,14 @@ appropriate for the sensor, in addition to the raw voltage signal returned by the sensor. Not all sensors are compatible with all boards. The developer(s) attempt to keep this list of known sensors up-to-date, but the code may provide additional sensors not listed here: -* __ADS1115 compatible__ (board can provide 3.3 V of power/reference to +* **ADS1115 compatible** (board can provide 3.3 V of power/reference to sensors): * voltage reading (V, mV) from any sensor that puts out a voltage in the range +/-3.3 V. * built-in thermistor (V, mV, K, C, F). * Vernier SS temperature probe (V, mV, K, C, F). -* __DAQC2 compatible__ (board can provide 5.0 V of power/reference to sensors): +* **DAQC2 compatible** (board can provide 5.0 V of power/reference to sensors): * voltage reading (V, mV) from any sensor that puts out a voltage in the range +/- 12 V. * Vernier SS temperature probe (V, mV, K, C, F). @@ -54,7 +54,7 @@ code may provide additional sensors not listed here: * Compatible with standard Vernier analog probes. Default calibrations being added as time and sensors become available. -* __LabQuest compatible__ (board provides 5.0 V of power/reference to sensors): +* **LabQuest compatible** (board provides 5.0 V of power/reference to sensors): * voltage reading (V, mV) from any sensor that puts out a voltage in the range +/- 10 V. * Vernier SS temperature probe (V, mV, K, C, F). diff --git a/docs/Change_Log.md b/docs/Change_Log.md index 4fda3ab..918418d 100644 --- a/docs/Change_Log.md +++ b/docs/Change_Log.md @@ -1,4 +1,9 @@ # Change Log +* 0.8.1 (May 2, 2023) + * BUG FIX: Now always waits for all data to be transferred to plot before + writing backup to a file. + * More rapid update of display when using LabQuests. + * Docs now recommend launching `jupyter nbclassic` for old notebook interface. * 0.8.0 (Apr. 20, 2023) * Replaced `NewRun()` command with `Run()` command. This version works in Jupyter Lab and removes the need for the `DisplayRun()` command because diff --git a/docs/Development_Notes.md b/docs/Development_Notes.md index 1231859..ca07d3d 100644 --- a/docs/Development_Notes.md +++ b/docs/Development_Notes.md @@ -9,7 +9,7 @@ pip and a Jupyter notebook. See: [python.org](https://python.org) and [Jupyter.org](https://jupyter.org). -1. If not installed, install pipenv:`$ pip3 install --user pipenv`. You may +1. If not installed, install pipenv:`pip3 install --user pipenv`. You may need to add `~/.local/bin` to your `PATH` to make `pipenv` available in your command shell. More discussion: [The Hitchhiker's Guide to Python](https://docs.python-guide.org/dev/virtualenvs/). @@ -17,21 +17,21 @@ available in your command shell. More discussion: or has been downloaded to. Use `pipenv`to install an ["editable" package](https://pip.pypa.io/en/stable/reference/pip_install/#editable-installs) inside the directory as described below: - 1. Start a shell in the environment `$ pipenv shell`. + 1. Start a shell in the environment `pipenv shell`. 1. Install using pip. 1. If you downloaded the git repository named "JupyterPiDAQ" and have used that directory to build your virtual - environment: `$ pip install -e ../JupyterPiDAQ/`. + environment: `pip install -e ../JupyterPiDAQ/`. 1. If you are downloading from PyPi - `$ pip install -e JupyterPiDAQ` + `pip install -e JupyterPiDAQ` 1. Either should install all the additional packages this package depends upon. On a Raspberry Pi this will take a long time. It probably will not run without at least 1 GB of swap. See: [Build Jupyter on a Pi ](https://www.uwosh.edu/facstaff/gutow/computer-and-programming-how-tos/installing-jupyter-on-raspberrian). - 1. Still within the environment shell test - this by starting jupyter `$ jupyter notebook`. Jupyter should launch in - your browser. + 1. Still within the environment shell test this by starting jupyter + `jupyter nbclassic` or `jupyter lab`. Jupyter should launch in your + browser. 1. Open a new notebook using the default (Python 3) kernel. 1. In the first cell import all from DAQinstance.py: `from jupyterpidaq.DAQinstance import *`. @@ -43,10 +43,10 @@ inside the directory as described below: possible to run this on any computer, not just a Pi. 1. If you wish, you can make this environment available to an alternate Jupyter install as a special kernel when you are the user. - 1. Make sure you are running in your virtual environment `$ pipenv shell` + 1. Make sure you are running in your virtual environment `pipenv shell` in the directory for virtual environment will do that. 1. Issue the command to add this as a kernel to your personal space: - `$ python -m ipykernel install --user --name=`. + `python -m ipykernel install --user --name=`. 1. More information is available in the Jupyter/Ipython documentation. A simple tutorial from Nikolai Jankiev (_Parametric Thoughts_) can be found [here](https://janakiev.com/til/jupyter-virtual-envs/). diff --git a/docs/User_Guide.md b/docs/User_Guide.md index 81f7e3e..e59b780 100644 --- a/docs/User_Guide.md +++ b/docs/User_Guide.md @@ -42,7 +42,8 @@ up, that lead to slightly different steps for starting the software: [Installation Instructions](https://jupyterphysscilab.github.io/JupyterPiDAQ/jupyterpidaq.html#installation)). * In this case launch Jupyter, in whichever directory you want to work, using the - command: `jupyter notebook` or `jupyter lab`. + command: `jupyter nbclassic` (for the old notebook interface) or `jupyter + lab` (for the newer lab interface). * Open a new notebook and choose the kernel for `JupyterPiDAQ`. The kernel name will depend upon what was chosen during installation. @@ -54,7 +55,8 @@ up, that lead to slightly different steps for starting the software: * Then enter the virtual environment with the command `pipenv shell`. This assumes you set up `pipenv` as described in the [Installation instructions](https://jupyterphysscilab.github.io/JupyterPiDAQ/jupyterpidaq.html#installation). - * Launch Jupyter using the command: `jupyter notebook` or `jupyter lab`. + * Launch Jupyter using the command: `jupyter nbclassic` (for the old + notebook interface) or `jupyter lab` (for the newer lab interface). * Open a new python notebook. ### Initialize the Data Acquisition Tools diff --git a/docs/jupyterpidaq.html b/docs/jupyterpidaq.html index 5764bf6..2e617cb 100644 --- a/docs/jupyterpidaq.html +++ b/docs/jupyterpidaq.html @@ -3,7 +3,7 @@ - + jupyterpidaq API documentation @@ -68,7 +68,7 @@

API Documentation

-
JupyterPiDAQ v0.8.0
+
JupyterPiDAQ v0.8.1
built with pdocStarting JupyterPiDAQ @@ -490,7 +492,7 @@

Setting up Development EnvironmentJupyter.org.

    -
  1. If not installed, install pipenv:$ pip3 install --user pipenv. You may +
  2. If not installed, install pipenv:pip3 install --user pipenv. You may need to add ~/.local/bin to your PATH to make pipenv available in your command shell. More discussion: The Hitchhiker's Guide to Python.
  3. @@ -499,23 +501,23 @@

    Setting up Development Environment"editable" package inside the directory as described below:
      -
    1. Start a shell in the environment $ pipenv shell.
    2. +
    3. Start a shell in the environment pipenv shell.
    4. Install using pip.
      1. If you downloaded the git repository named "JupyterPiDAQ" and have used that directory to build your virtual -environment: $ pip install -e ../JupyterPiDAQ/.
      2. +environment: pip install -e ../JupyterPiDAQ/.
      3. If you are downloading from PyPi -$ pip install -e JupyterPiDAQ
      4. +pip install -e JupyterPiDAQ
      5. Either should install all the additional packages this package depends upon. On a Raspberry Pi this will take a long time. It probably will not run without at least 1 GB of swap. See: Build Jupyter on a Pi .
    5. -
    6. Still within the environment shell test -this by starting jupyter $ jupyter notebook. Jupyter should launch in -your browser. +
    7. Still within the environment shell test this by starting jupyter +jupyter nbclassic or jupyter lab. Jupyter should launch in your +browser.
      1. Open a new notebook using the default (Python 3) kernel.
      2. In the first cell import all from DAQinstance.py: @@ -531,10 +533,10 @@

        Setting up Development EnvironmentIf you wish, you can make this environment available to an alternate Jupyter install as a special kernel when you are the user.
          -
        1. Make sure you are running in your virtual environment $ pipenv shell +
        2. Make sure you are running in your virtual environment pipenv shell in the directory for virtual environment will do that.
        3. Issue the command to add this as a kernel to your personal space: -$ python -m ipykernel install --user --name=<name-you-want-for-kernel>.
        4. +python -m ipykernel install --user --name=<name-you-want-for-kernel>.
        5. More information is available in the Jupyter/Ipython documentation. A simple tutorial from Nikolai Jankiev (_Parametric Thoughts_) can be found here.
        6. @@ -630,6 +632,13 @@

          Releasing on PyPi

          Change Log

          -
          JupyterPiDAQ v0.8.0
          +
          JupyterPiDAQ v0.8.1
          built with pdoc - + jupyterpidaq.Boards.PiGPIO.ADS1115 API documentation @@ -58,7 +58,7 @@

          API Documentation

          -
          JupyterPiDAQ v0.8.0
          +
          JupyterPiDAQ v0.8.1
          built with pdoc - + jupyterpidaq.Boards.PiGPIO.DAQC2 API documentation @@ -58,7 +58,7 @@

          API Documentation

          -
          JupyterPiDAQ v0.8.0
          +
          JupyterPiDAQ v0.8.1
          built with pdoc - + jupyterpidaq.Boards.Simulated API documentation @@ -35,7 +35,7 @@

          Submodules

          -
          JupyterPiDAQ v0.8.0
          +
          JupyterPiDAQ v0.8.1
          built with pdoc - + jupyterpidaq.Boards.Simulated.ADCsim API documentation @@ -58,7 +58,7 @@

          API Documentation

          -
          JupyterPiDAQ v0.8.0
          +
          JupyterPiDAQ v0.8.1
          built with pdoc - + jupyterpidaq.Boards.Simulated.ADCsim_line API documentation @@ -58,7 +58,7 @@

          API Documentation

          -
          JupyterPiDAQ v0.8.0
          +
          JupyterPiDAQ v0.8.1
          built with pdoc - + jupyterpidaq.Boards.boards API documentation @@ -76,7 +76,7 @@

          API Documentation

          -
          JupyterPiDAQ v0.8.0
          +
          JupyterPiDAQ v0.8.1
          built with pdoc - + jupyterpidaq.ChannelSettings API documentation @@ -73,7 +73,7 @@

          API Documentation

          -
          JupyterPiDAQ v0.8.0
          +
          JupyterPiDAQ v0.8.1
          built with pdoc - + jupyterpidaq.DAQProc API documentation @@ -37,7 +37,7 @@

          API Documentation

          -
          JupyterPiDAQ v0.8.0
          +
          JupyterPiDAQ v0.8.1
          built with pdoc - + jupyterpidaq.DAQinstance API documentation @@ -82,7 +82,7 @@

          API Documentation

          -
          JupyterPiDAQ v0.8.0
          +
          JupyterPiDAQ v0.8.1
          built with pdoc 20logname = 'DAQinstance_' + time.strftime('%y-%m-%d_%H%M%S', 21 time.localtime()) + '.log' 22logging.basicConfig(filename=logname, level=logging.WARN) - 23 - 24# below is equivalent to %matplotlib notebook in a Jupyter cell - 25from IPython import get_ipython - 26 - 27ipython = get_ipython() - 28print('Importing drivers and searching for available data acquisition ' - 29 'hardware.',end='') - 30 - 31# imports below must work. Allow normal python error response. - 32import ipywidgets as widgets - 33from pandas_GUI import * - 34print ('.',end='') - 35 - 36from plotly import io as pio - 37pio.templates.default = "simple_white" #default plot format - 38from plotly import graph_objects as go - 39print ('.',end='') - 40 - 41import numpy as np - 42print ('.',end='') - 43 - 44import pandas as pd - 45print ('.',end='') - 46 - 47from IPython.display import display, HTML - 48from IPython.display import Javascript as JS - 49 - 50print ('.',end='') - 51 - 52# Below allows asynchronous calls to get and plot the data in real time. - 53# Actually read the DAQ board on a different process. - 54import threading - 55from multiprocessing import Process, Pipe - 56 - 57print('.',end='') - 58 - 59# The process that monitors the board - 60from jupyterpidaq.DAQProc import DAQProc - 61 - 62print('.',end='') - 63 - 64# Board definitions - 65from jupyterpidaq import Boards as boards - 66 - 67print('.',end='') - 68 - 69global availboards - 70availboards = boards.load_boards() - 71 - 72print('.',end='') - 73 - 74# GUI for settings - 75from jupyterpidaq.ChannelSettings import ChannelSettings - 76 - 77print('.',end='') - 78 - 79# Sensor definitions - 80from jupyterpidaq.Sensors import sensors - 81 - 82print('.',end='') - 83 - 84# JPSL Utilities - 85from JPSLMenus import * - 86from JPSLUtils import * - 87 - 88print('.',end='') - 89 - 90# globals to put stuff in from threads. - 91data = [] # all data from DAQ tools avg_values - 92stdev = [] # all standard deviations - 93timestamp = [] # all timestamps - 94 - 95# global list to keep track of runs - 96runs = [] - 97 - 98###### - 99# Interactive elements definitions -100###### -101 -102# Locate JupyterPiDAQ package directory -103mydir = os.path.dirname( -104 __file__) # absolute path to directory containing this file. -105 -106# Add a "DAQ Menu" to the notebook. -107tempJSfile = open(os.path.join(mydir, 'javascript', 'JupyterPiDAQmnu.js')) -108tempscript = '<script type="text/javascript">' -109tempscript += tempJSfile.read() + '</script>' -110tempJSfile.close() -111display(HTML(tempscript)) -112JPSLUtils.OTJS('createCmdMenu()') -113 -114print('Done with setup.') -115 -116# cleanup log file if it is empty -117logging.shutdown() -118try: -119 if os.stat(logname).st_size == 0: -120 os.remove(logname) -121except FileNotFoundError: -122 pass -123 -124# Data Aquistion Instance (a run). -125class DAQinstance(): -126 def __init__(self, idno, title='None', ntraces=4, **kwargs): -127 """ -128 Data Aquistion Instance (a run). -129 -130 :param idno : id number you wish to use to keep track -131 :param title: optional name -132 :param ntraces: number of traces (default = 4) more than 4 easily -133 overwhelms a pi4. -134 :param kwargs: -135 :ignore_skew: bool (default: True) if True only a single average -136 collection time will be recorded for each time in a multichannel -137 data collection. If False a separate set of time will be -138 recorded for each channel. -139 """ -140 from plotly import graph_objects as go -141 self.ignore_skew = kwargs.pop('ignore_skew',True) -142 self.idno = idno -143 self.livefig = go.FigureWidget(layout_template='simple_white') -144 self.title = str(title) -145 self.svname = title + '.jpidaq.html' -146 self.averaging_time = 0.1 # seconds adjusted based on collection rate -147 self.gain = [1] * ntraces -148 self.data = [] -149 self.timestamp = [] -150 self.stdev = [] -151 self.pandadf = None -152 self.ntraces = ntraces -153 self.separate_plots = True -154 self.traces = [] -155 # index map from returned data to trace, -156 self.tracemap = [] -157 self.tracefrdatachn = [] -158 self.tracelbls = [] -159 self.units = [] -160 for i in range(self.ntraces): -161 self.traces.append(ChannelSettings(i, availboards)) -162 self.ratemax = 20.0 # Hz -163 self.rate = 1.0 # Hz -164 self.deltamin = 1 / self.ratemax -165 self.delta = 1.0 / self.rate -166 self.setupbtn = widgets.Button( -167 description='Set Parameters', -168 disabled=False, -169 button_style='info', -170 # 'success', 'info', 'warning', 'danger' or '' -171 tooltip='Click to set collection parameters to displayed values.', -172 icon='') -173 self.collectbtn = widgets.Button( -174 description='Start Collecting', -175 disabled=False, -176 button_style='success', -177 # 'success', 'info', 'warning', 'danger' or '' -178 tooltip='Start collecting data and plotting it. ' -179 'Will make new graph.', -180 icon='') -181 self.separate_traces_checkbox = widgets.Checkbox( -182 value = self.separate_plots, -183 description = 'Display multiple traces as stacked graphs. ' \ -184 'Uncheck to display all on the same graph.', -185 layout = widgets.Layout(width='80%'), -186 disabled = False) -187 self.rateinp = widgets.BoundedFloatText( -188 value=self.rate, -189 min=0, -190 max=self.ratemax, -191 step=self.ratemax / 1000.0, -192 description='Rate (Hz):', -193 disabled=False) -194 self.timelbl = widgets.Text( -195 value='Time(s)', -196 placeholder='Type something', -197 description='X-axis label (time):', + 23# logging.basicConfig(filename=logname, level=logging.DEBUG) + 24 + 25# below is equivalent to %matplotlib notebook in a Jupyter cell + 26from IPython import get_ipython + 27 + 28ipython = get_ipython() + 29print('Importing drivers and searching for available data acquisition ' + 30 'hardware.',end='') + 31 + 32# imports below must work. Allow normal python error response. + 33import ipywidgets as widgets + 34from pandas_GUI import * + 35print ('.',end='') + 36 + 37from plotly import io as pio + 38pio.templates.default = "simple_white" #default plot format + 39from plotly import graph_objects as go + 40print ('.',end='') + 41 + 42import numpy as np + 43print ('.',end='') + 44 + 45import pandas as pd + 46print ('.',end='') + 47 + 48from IPython.display import display, HTML + 49from IPython.display import Javascript as JS + 50 + 51print ('.',end='') + 52 + 53# Below allows asynchronous calls to get and plot the data in real time. + 54# Actually read the DAQ board on a different process. + 55import threading + 56from multiprocessing import Process, Pipe + 57 + 58print('.',end='') + 59 + 60# The process that monitors the board + 61from jupyterpidaq.DAQProc import DAQProc + 62 + 63print('.',end='') + 64 + 65# Board definitions + 66from jupyterpidaq import Boards as boards + 67 + 68print('.',end='') + 69 + 70global availboards + 71availboards = boards.load_boards() + 72 + 73print('.',end='') + 74 + 75# GUI for settings + 76from jupyterpidaq.ChannelSettings import ChannelSettings + 77 + 78print('.',end='') + 79 + 80# Sensor definitions + 81from jupyterpidaq.Sensors import sensors + 82 + 83print('.',end='') + 84 + 85# JPSL Utilities + 86from JPSLMenus import * + 87from JPSLUtils import * + 88 + 89print('.',end='') + 90 + 91# globals to put stuff in from threads. + 92data = [] # all data from DAQ tools avg_values + 93stdev = [] # all standard deviations + 94timestamp = [] # all timestamps + 95 + 96# global list to keep track of runs + 97runs = [] + 98 + 99###### +100# Interactive elements definitions +101###### +102 +103# Locate JupyterPiDAQ package directory +104mydir = os.path.dirname( +105 __file__) # absolute path to directory containing this file. +106 +107# Add a "DAQ Menu" to the notebook. +108tempJSfile = open(os.path.join(mydir, 'javascript', 'JupyterPiDAQmnu.js')) +109tempscript = '<script type="text/javascript">' +110tempscript += tempJSfile.read() + '</script>' +111tempJSfile.close() +112display(HTML(tempscript)) +113JPSLUtils.OTJS('createCmdMenu()') +114 +115print('Done with setup.') +116 +117# cleanup log file if it is empty +118logging.shutdown() +119try: +120 if os.stat(logname).st_size == 0: +121 os.remove(logname) +122except FileNotFoundError: +123 pass +124 +125# Data Aquistion Instance (a run). +126class DAQinstance(): +127 def __init__(self, idno, title='None', ntraces=4, **kwargs): +128 """ +129 Data Aquistion Instance (a run). +130 +131 :param idno : id number you wish to use to keep track +132 :param title: optional name +133 :param ntraces: number of traces (default = 4) more than 4 easily +134 overwhelms a pi4. +135 :param kwargs: +136 :ignore_skew: bool (default: True) if True only a single average +137 collection time will be recorded for each time in a multichannel +138 data collection. If False a separate set of time will be +139 recorded for each channel. +140 """ +141 from plotly import graph_objects as go +142 self.ignore_skew = kwargs.pop('ignore_skew',True) +143 self.idno = idno +144 self.livefig = go.FigureWidget(layout_template='simple_white') +145 self.PLTconn, self.DAQconn = Pipe() +146 self.DAQCTL, self.PLTCTL = Pipe() +147 self.pltthread = threading.Thread(target=self.updatingplot, args=( +148 self.PLTconn, self.PLTCTL)) +149 self.title = str(title) +150 self.svname = title + '.jpidaq.html' +151 self.averaging_time = 0.1 # seconds adjusted based on collection rate +152 self.gain = [1] * ntraces +153 self.data = [] +154 self.timestamp = [] +155 self.stdev = [] +156 self.pandadf = None +157 self.ntraces = ntraces +158 self.separate_plots = True +159 self.traces = [] +160 # index map from returned data to trace, +161 self.tracemap = [] +162 self.tracefrdatachn = [] +163 self.tracelbls = [] +164 self.units = [] +165 for i in range(self.ntraces): +166 self.traces.append(ChannelSettings(i, availboards)) +167 self.ratemax = 20.0 # Hz +168 self.rate = 1.0 # Hz +169 self.deltamin = 1 / self.ratemax +170 self.delta = 1.0 / self.rate +171 self.setupbtn = widgets.Button( +172 description='Set Parameters', +173 disabled=False, +174 button_style='info', +175 # 'success', 'info', 'warning', 'danger' or '' +176 tooltip='Click to set collection parameters to displayed values.', +177 icon='') +178 self.collectbtn = widgets.Button( +179 description='Start Collecting', +180 disabled=False, +181 button_style='success', +182 # 'success', 'info', 'warning', 'danger' or '' +183 tooltip='Start collecting data and plotting it. ' +184 'Will make new graph.', +185 icon='') +186 self.separate_traces_checkbox = widgets.Checkbox( +187 value = self.separate_plots, +188 description = 'Display multiple traces as stacked graphs. ' \ +189 'Uncheck to display all on the same graph.', +190 layout = widgets.Layout(width='80%'), +191 disabled = False) +192 self.rateinp = widgets.BoundedFloatText( +193 value=self.rate, +194 min=0, +195 max=self.ratemax, +196 step=self.ratemax / 1000.0, +197 description='Rate (Hz):', 198 disabled=False) -199 self.runtitle = widgets.Text( -200 value=self.title, -201 placeholder='Type title/description', -202 description='Run title', +199 self.timelbl = widgets.Text( +200 value='Time(s)', +201 placeholder='Type something', +202 description='X-axis label (time):', 203 disabled=False) -204 self.defaultparamtxt = '' -205 self.defaultcollecttxt = '<span style="color:blue;"> To accurately ' -206 self.defaultcollecttxt += 'read point location on graph you can zoom ' -207 self.defaultcollecttxt += 'in. Drag to zoom. Additional controls show ' -208 self.defaultcollecttxt += 'above plot when you hover over it.</span>' -209 self.collecttxt = widgets.HTML( -210 value=self.defaultcollecttxt, -211 placeholder='', -212 description='') -213 self.setup_layout_bottom = widgets.HBox( -214 [self.rateinp, self.timelbl, self.setupbtn]) -215 self.setup_layout = widgets.VBox([self.separate_traces_checkbox, -216 self.setup_layout_bottom]) -217 self.collect_layout = widgets.HBox([self.collectbtn, self.collecttxt]) -218 self.output = widgets.Output() -219 def _make_defaultparamtxt(self): -220 """ -221 Uses AdvancedHTMLParser (mimics javascript) to generate valid HTML for -222 the default parameter text. -223 :return: valid html string for the default parameter text. -224 """ -225 from AdvancedHTMLParser import AdvancedTag as domel -226 run_info=domel('div') -227 run_info.setAttribute('id','DAQRun_' + str(self.idno) + '_info') -228 run_info.setAttribute('class','run_info') -229 run_id = domel('table') -230 run_id.setAttribute('id','run_id') -231 run_id.setAttribute('border', '1') -232 tr = domel('tr') -233 tr.appendInnerHTML('<th>Title</th><th>Id #</th>') -234 run_id.appendChild(tr) -235 tr = domel('tr') -236 tr.appendInnerHTML('<td>' + str(self.title) + '</td>' \ -237 '<td>' + str(self.idno) + '</td>') -238 run_id.appendChild(tr) -239 run_info.appendChild(run_id) -240 # table of run parameters -241 run_param = domel('table') -242 run_param.setAttribute('border','1') -243 run_param.setAttribute('id','run_param') -244 tr = domel('tr') -245 tr.setAttribute('style','text-align:center;') -246 tr.appendInnerHTML('<th>Approx. Rate (Hz)</th>' \ -247 '<th>Approx. Delta (s)</th>' \ -248 '<th>X-label </th>' \ -249 '<th>X-cols</th>' \ -250 '<th>Y-cols</th>' \ -251 '<th>err-cols<sup ' \ -252 'style="color:blue;">a</sup></th>' \ -253 '<th>One Plot</th>' ) -254 run_param.appendChild(tr) -255 run_info.appendChild(run_param) -256 tr = domel('tr') -257 tr.setAttribute('style','text-align:center;') -258 tr.appendInnerHTML('<td>' + str(self.rate) + '</td>' \ -259 '<td>' + str(self.delta) + '</td>' \ -260 '<td>' + self.timelbl.value + '</td>') -261 xlist = '[' -262 ylist = '[' -263 errlist = '[' -264 if self.ignore_skew: -265 xlist += '0]' -266 tempcount = 0 -267 for k in self.traces: -268 if k.isactive: -269 ylist += str(2 * tempcount + 1) + ',' -270 errlist += str(2 * tempcount + 2) + ',' -271 tempcount += 1 -272 ylist = ylist[:-1] + ']' -273 errlist = errlist[:-1] + ']' -274 else: -275 tempcount = 0 -276 for k in self.traces: -277 if k.isactive: -278 xlist += str(3 * tempcount) + ',' -279 ylist += str(3 * tempcount + 1) + ',' -280 errlist += str(3 * tempcount + 2) + ',' -281 tempcount += 1 -282 xlist = xlist[:-1] + ']' -283 ylist = ylist[:-1] + ']' -284 errlist = errlist[:-1] + ']' -285 tr.appendInnerHTML('<td>' + xlist + '</td><td>' + ylist + '</td>') -286 td = domel('td') -287 td.appendText(errlist) -288 tr.appendChild(td) -289 td = domel('td') -290 td.appendText(str(not (self.separate_plots))) -291 tr.appendChild(td) -292 run_param.appendChild(tr) -293 footer = domel('tfoot') -294 tr = domel('tr') -295 td = domel('td') -296 td.setAttribute('colspan','7') -297 td.appendInnerHTML('<sup style="color:blue;">a</sup>The ' \ -298 'standard deviation of the number in the ' \ -299 'column immediately to the left based on the ' \ -300 'variation in signal during the averaging time ' \ -301 'for the data point.') -302 tr.appendChild(td) -303 footer.appendChild(tr) -304 run_param.appendChild(footer) -305 # table of trace information -306 traceinfo = domel('table') -307 traceinfo.setAttribute('class','traceinfo') -308 traceinfo.setAttribute('id','traceinfo') -309 traceinfo.setAttribute('border','1') -310 tr = domel('tr') -311 tr.setAttribute('style','text-align:center;') -312 tr.appendInnerHTML('<th>Trace #</th><th>Title</th><th>Units</th>' \ -313 '<th>Board</th><th>Channel</th><th>Sensor</th>' \ -314 '<th>Gain</th>') -315 traceinfo.appendChild(tr) -316 for i in range(self.ntraces): -317 if (self.traces[i].isactive): -318 self.tracemap.append(i) -319 tr = domel('tr') -320 tr.setAttribute('style', 'text-align:center;') -321 tr.appendInnerHTML('<td>' + str(i) + '</td>' \ -322 '<td>' + self.traces[i].tracelbl.value + '</td>' \ -323 '<td>' + self.traces[i].units.value + '</td>' \ -324 '<td>' + str(self.traces[i].boardchoice.value) + \ -325 ' ' + self.traces[i].board.name + '</td>' \ -326 '<td>' + str(self.traces[i].channel) + '</td>' \ -327 '<td >' + self.traces[i].sensorchoice.value + '</td>' \ -328 '<td>' + str(self.traces[i].gains.value) + '</td>') -329 traceinfo.appendChild(tr) -330 run_info.appendChild(traceinfo) -331 return run_info.asHTML() -332 -333 def _load_from_html(self, file): -334 """ -335 Loads data and parameters for a completed run from a saved html file. -336 :param file: filename or path. -337 :return: -338 """ -339 import pandas as pd -340 from JPSLUtils import find_pandas_dataframe_names -341 from AdvancedHTMLParser import AdvancedHTMLParser as parser -342 from plotly import graph_objects as go -343 whichrun = pd.read_html(file, attrs={'id': 'run_id'})[0] -344 run_title = whichrun['Title'][0] -345 run_id = whichrun['Id #'][0] -346 svname = pd.read_html(file, attrs={'id': 'file_info'})[0]['Saved ' \ -347 'as'][0] -348 self.pandadf = pd.read_html(file, attrs={'class': 'dataframe'}, -349 index_col=0)[0] -350 self.title = run_title -351 self.svname = svname -352 run_param = \ -353 pd.read_html(file, attrs={'id': 'run_param'}, skiprows=[2])[0] -354 self.rate = run_param['Approx. Rate (Hz)'][0] -355 self.delta = run_param['Approx. Delta (s)'][0] -356 # reassiging timelbl to a value from a widget -357 self.timelbl = run_param['X-label'][0] -358 self.separate_plots = not (run_param['One Plot'][0]) -359 xcols = list(map(int,run_param['X-cols'][0].replace('[', -360 '').replace(']','').split(','))) -361 ycols = list(map(int,run_param['Y-cols'][0].replace('[', -362 '').replace(']','').split(','))) -363 errcols = list(map(int,run_param['err-colsa'][0].replace('[', -364 '').replace(']', '').split(','))) -365 htmldatafile = parser(file) -366 self.defaultparamtxt = htmldatafile.getElementsByClassName( -367 'run_info')[0].asHTML() -368 traceinfo = pd.read_html(file, attrs={'id': 'traceinfo'})[0] -369 for k in traceinfo.index: -370 # Do not refill the widgets. This truncates and changes the -371 # definitions of some things from widgets to values. -372 self.traces[k].isactive = True -373 self.traces[k].tracelbl= traceinfo['Title'][k] -374 self.traces[k].units = traceinfo['Units'][k] -375 boardchoice, boardname = (traceinfo['Board'][k]).split(' ',1) -376 self.traces[k].boardchoice = boardchoice -377 self.traces[k].board = boardname -378 self.traces[k].channel = traceinfo['Channel'][k] -379 self.traces[k].gains= traceinfo['Gain'][k] -380 self.traces[k].sensor = traceinfo['Sensor'][k] -381 # Plot -382 if self.separate_plots: -383 self.livefig.set_subplots(rows=len(ycols), cols=1, -384 shared_xaxes=True) -385 self.livefig.update_xaxes( -386 title=self.timelbl, row=len(ycols), col=1) -387 else: -388 self.livefig.update_xaxes(title=self.timelbl) -389 self.livefig.update_yaxes(title="Values") -390 for i in range(len(ycols)): -391 namestr = self.pandadf.columns[ycols[i]] -392 xcol = None -393 if len(xcols) == 1: -394 xcol = xcols[0] -395 else: -396 xcol = xcols[i] -397 scat = go.Scatter( -398 y=self.pandadf.iloc[0:, ycols[i]], -399 x=self.pandadf.iloc[0:,xcol], name=namestr) -400 if self.separate_plots: -401 self.livefig.update_yaxes(title=self.traces[i].units, -402 row=i+1, col=1) -403 self.livefig.add_trace(scat, row=i+1, col=1) -404 else: -405 self.livefig.add_trace(scat) -406 pass -407 -408 def setupclick(self, btn): -409 # Could just use the values in widgets, but this forces intentional -410 # selection and locks them for the run. -411 from copy import copy -412 self.title = copy(self.runtitle.value) -413 self.rate = copy(self.rateinp.value) -414 self.delta = 1 / self.rate -415 self.separate_plots = copy(self.separate_traces_checkbox.value) -416 self.defaultparamtxt = self._make_defaultparamtxt() -417 self.runtitle.close() -418 del self.runtitle -419 self.setup_layout.close() -420 del self.setup_layout -421 self.output.clear_output() -422 doRun(runs[self.idno-1]) -423 with self.output: -424 display(runs[self.idno - 1].livefig) -425 pass -426 -427 def setup(self): +204 self.runtitle = widgets.Text( +205 value=self.title, +206 placeholder='Type title/description', +207 description='Run title', +208 disabled=False) +209 self.defaultparamtxt = '' +210 self.defaultcollecttxt = '<span style="color:blue;"> To accurately ' +211 self.defaultcollecttxt += 'read point location on graph you can zoom ' +212 self.defaultcollecttxt += 'in. Drag to zoom. Additional controls show ' +213 self.defaultcollecttxt += 'above plot when you hover over it.</span>' +214 self.collecttxt = widgets.HTML( +215 value=self.defaultcollecttxt, +216 placeholder='', +217 description='') +218 self.setup_layout_bottom = widgets.HBox( +219 [self.rateinp, self.timelbl, self.setupbtn]) +220 self.setup_layout = widgets.VBox([self.separate_traces_checkbox, +221 self.setup_layout_bottom]) +222 self.collect_layout = widgets.HBox([self.collectbtn, self.collecttxt]) +223 self.output = widgets.Output() +224 def _make_defaultparamtxt(self): +225 """ +226 Uses AdvancedHTMLParser (mimics javascript) to generate valid HTML for +227 the default parameter text. +228 :return: valid html string for the default parameter text. +229 """ +230 from AdvancedHTMLParser import AdvancedTag as domel +231 run_info=domel('div') +232 run_info.setAttribute('id','DAQRun_' + str(self.idno) + '_info') +233 run_info.setAttribute('class','run_info') +234 run_id = domel('table') +235 run_id.setAttribute('id','run_id') +236 run_id.setAttribute('border', '1') +237 tr = domel('tr') +238 tr.appendInnerHTML('<th>Title</th><th>Id #</th>') +239 run_id.appendChild(tr) +240 tr = domel('tr') +241 tr.appendInnerHTML('<td>' + str(self.title) + '</td>' \ +242 '<td>' + str(self.idno) + '</td>') +243 run_id.appendChild(tr) +244 run_info.appendChild(run_id) +245 # table of run parameters +246 run_param = domel('table') +247 run_param.setAttribute('border','1') +248 run_param.setAttribute('id','run_param') +249 tr = domel('tr') +250 tr.setAttribute('style','text-align:center;') +251 tr.appendInnerHTML('<th>Approx. Rate (Hz)</th>' \ +252 '<th>Approx. Delta (s)</th>' \ +253 '<th>X-label </th>' \ +254 '<th>X-cols</th>' \ +255 '<th>Y-cols</th>' \ +256 '<th>err-cols<sup ' \ +257 'style="color:blue;">a</sup></th>' \ +258 '<th>One Plot</th>' ) +259 run_param.appendChild(tr) +260 run_info.appendChild(run_param) +261 tr = domel('tr') +262 tr.setAttribute('style','text-align:center;') +263 tr.appendInnerHTML('<td>' + str(self.rate) + '</td>' \ +264 '<td>' + str(self.delta) + '</td>' \ +265 '<td>' + self.timelbl.value + '</td>') +266 xlist = '[' +267 ylist = '[' +268 errlist = '[' +269 if self.ignore_skew: +270 xlist += '0]' +271 tempcount = 0 +272 for k in self.traces: +273 if k.isactive: +274 ylist += str(2 * tempcount + 1) + ',' +275 errlist += str(2 * tempcount + 2) + ',' +276 tempcount += 1 +277 ylist = ylist[:-1] + ']' +278 errlist = errlist[:-1] + ']' +279 else: +280 tempcount = 0 +281 for k in self.traces: +282 if k.isactive: +283 xlist += str(3 * tempcount) + ',' +284 ylist += str(3 * tempcount + 1) + ',' +285 errlist += str(3 * tempcount + 2) + ',' +286 tempcount += 1 +287 xlist = xlist[:-1] + ']' +288 ylist = ylist[:-1] + ']' +289 errlist = errlist[:-1] + ']' +290 tr.appendInnerHTML('<td>' + xlist + '</td><td>' + ylist + '</td>') +291 td = domel('td') +292 td.appendText(errlist) +293 tr.appendChild(td) +294 td = domel('td') +295 td.appendText(str(not (self.separate_plots))) +296 tr.appendChild(td) +297 run_param.appendChild(tr) +298 footer = domel('tfoot') +299 tr = domel('tr') +300 td = domel('td') +301 td.setAttribute('colspan','7') +302 td.appendInnerHTML('<sup style="color:blue;">a</sup>The ' \ +303 'standard deviation of the number in the ' \ +304 'column immediately to the left based on the ' \ +305 'variation in signal during the averaging time ' \ +306 'for the data point.') +307 tr.appendChild(td) +308 footer.appendChild(tr) +309 run_param.appendChild(footer) +310 # table of trace information +311 traceinfo = domel('table') +312 traceinfo.setAttribute('class','traceinfo') +313 traceinfo.setAttribute('id','traceinfo') +314 traceinfo.setAttribute('border','1') +315 tr = domel('tr') +316 tr.setAttribute('style','text-align:center;') +317 tr.appendInnerHTML('<th>Trace #</th><th>Title</th><th>Units</th>' \ +318 '<th>Board</th><th>Channel</th><th>Sensor</th>' \ +319 '<th>Gain</th>') +320 traceinfo.appendChild(tr) +321 for i in range(self.ntraces): +322 if (self.traces[i].isactive): +323 self.tracemap.append(i) +324 tr = domel('tr') +325 tr.setAttribute('style', 'text-align:center;') +326 tr.appendInnerHTML('<td>' + str(i) + '</td>' \ +327 '<td>' + self.traces[i].tracelbl.value + '</td>' \ +328 '<td>' + self.traces[i].units.value + '</td>' \ +329 '<td>' + str(self.traces[i].boardchoice.value) + \ +330 ' ' + self.traces[i].board.name + '</td>' \ +331 '<td>' + str(self.traces[i].channel) + '</td>' \ +332 '<td >' + self.traces[i].sensorchoice.value + '</td>' \ +333 '<td>' + str(self.traces[i].gains.value) + '</td>') +334 traceinfo.appendChild(tr) +335 run_info.appendChild(traceinfo) +336 return run_info.asHTML() +337 +338 def _load_from_html(self, file): +339 """ +340 Loads data and parameters for a completed run from a saved html file. +341 :param file: filename or path. +342 :return: +343 """ +344 import pandas as pd +345 from JPSLUtils import find_pandas_dataframe_names +346 from AdvancedHTMLParser import AdvancedHTMLParser as parser +347 from plotly import graph_objects as go +348 whichrun = pd.read_html(file, attrs={'id': 'run_id'})[0] +349 run_title = whichrun['Title'][0] +350 run_id = whichrun['Id #'][0] +351 svname = pd.read_html(file, attrs={'id': 'file_info'})[0]['Saved ' \ +352 'as'][0] +353 self.pandadf = pd.read_html(file, attrs={'class': 'dataframe'}, +354 index_col=0)[0] +355 self.title = run_title +356 self.svname = svname +357 run_param = \ +358 pd.read_html(file, attrs={'id': 'run_param'}, skiprows=[2])[0] +359 self.rate = run_param['Approx. Rate (Hz)'][0] +360 self.delta = run_param['Approx. Delta (s)'][0] +361 # reassiging timelbl to a value from a widget +362 self.timelbl = run_param['X-label'][0] +363 self.separate_plots = not (run_param['One Plot'][0]) +364 xcols = list(map(int,run_param['X-cols'][0].replace('[', +365 '').replace(']','').split(','))) +366 ycols = list(map(int,run_param['Y-cols'][0].replace('[', +367 '').replace(']','').split(','))) +368 errcols = list(map(int,run_param['err-colsa'][0].replace('[', +369 '').replace(']', '').split(','))) +370 htmldatafile = parser(file) +371 self.defaultparamtxt = htmldatafile.getElementsByClassName( +372 'run_info')[0].asHTML() +373 traceinfo = pd.read_html(file, attrs={'id': 'traceinfo'})[0] +374 for k in traceinfo.index: +375 # Do not refill the widgets. This truncates and changes the +376 # definitions of some things from widgets to values. +377 self.traces[k].isactive = True +378 self.traces[k].tracelbl= traceinfo['Title'][k] +379 self.traces[k].units = traceinfo['Units'][k] +380 boardchoice, boardname = (traceinfo['Board'][k]).split(' ',1) +381 self.traces[k].boardchoice = boardchoice +382 self.traces[k].board = boardname +383 self.traces[k].channel = traceinfo['Channel'][k] +384 self.traces[k].gains= traceinfo['Gain'][k] +385 self.traces[k].sensor = traceinfo['Sensor'][k] +386 # Plot +387 if self.separate_plots: +388 self.livefig.set_subplots(rows=len(ycols), cols=1, +389 shared_xaxes=True) +390 self.livefig.update_xaxes( +391 title=self.timelbl, row=len(ycols), col=1) +392 else: +393 self.livefig.update_xaxes(title=self.timelbl) +394 self.livefig.update_yaxes(title="Values") +395 for i in range(len(ycols)): +396 namestr = self.pandadf.columns[ycols[i]] +397 xcol = None +398 if len(xcols) == 1: +399 xcol = xcols[0] +400 else: +401 xcol = xcols[i] +402 scat = go.Scatter( +403 y=self.pandadf.iloc[0:, ycols[i]], +404 x=self.pandadf.iloc[0:,xcol], name=namestr) +405 if self.separate_plots: +406 self.livefig.update_yaxes(title=self.traces[i].units, +407 row=i+1, col=1) +408 self.livefig.add_trace(scat, row=i+1, col=1) +409 else: +410 self.livefig.add_trace(scat) +411 pass +412 +413 def setupclick(self, btn): +414 # Could just use the values in widgets, but this forces intentional +415 # selection and locks them for the run. +416 from copy import copy +417 self.title = copy(self.runtitle.value) +418 self.rate = copy(self.rateinp.value) +419 self.delta = 1 / self.rate +420 self.separate_plots = copy(self.separate_traces_checkbox.value) +421 self.defaultparamtxt = self._make_defaultparamtxt() +422 self.runtitle.close() +423 del self.runtitle +424 self.setup_layout.close() +425 del self.setup_layout +426 self.output.clear_output() +427 doRun(runs[self.idno-1]) 428 with self.output: -429 display(HTML("<h3 id ='RunSetUp' " -430 "style='text-align:center;'>Set Run Parameters</h3>")) -431 self.setupbtn.on_click(self.setupclick) -432 display(self.runtitle) -433 for i in range(self.ntraces): -434 self.traces[i].setup() -435 display(self.setup_layout) -436 display(self.output) -437 pass -438 -439 def collectclick(self, btn): -440 if (btn.description == 'Start Collecting'): -441 btn.description = 'Stop Collecting' -442 btn.button_style = 'danger' -443 btn.tooltip = 'Stop the data collection' -444 # do not allow parameters to be reset after starting run. -445 self.setupbtn.disabled = True -446 self.setupbtn.tooltip = 'Parameters locked. The run has started.' -447 self.rateinp.disabled = True -448 self.timelbl.disabled = True -449 PLTconn, DAQconn = Pipe() -450 DAQCTL, PLTCTL = Pipe() -451 nactive = 0 -452 for k in self.traces: -453 if k.isactive: -454 nactive += 1 -455 whichchn = [] -456 gains =[] -457 for i in range(self.ntraces): -458 if (self.traces[i].isactive): -459 brd = self.traces[i].board -460 chn = self.traces[i].channel -461 newchn = True -462 if len(whichchn) > 0: -463 for k in range(len(whichchn)): -464 if whichchn[k]['board'] == brd \ -465 and whichchn[k]['chnl'] == chn: -466 self.tracefrdatachn.append(k) -467 newchn = False -468 if newchn: -469 whichchn.append({'board': brd, -470 'chnl': chn}) -471 gains.append(self.traces[i].toselectedgain) -472 self.tracefrdatachn.append(len(whichchn)-1) -473 # Use up to 30% of the time for averaging if channels were spaced -474 # evenly between data collection times (with DACQ2 they appear -475 # more synchronous than that). -476 self.averaging_time = self.delta / nactive / 3 -477 DAQ = Process(target=DAQProc, -478 args=( -479 whichchn, gains, self.averaging_time, self.delta, -480 DAQconn, DAQCTL)) -481 DAQ.start() -482 thread = threading.Thread(target=self.updatingplot, args=( -483 PLTconn, PLTCTL)) -484 thread.start() -485 # self.updatingplot() hangs up user interface -486 else: -487 btn.description = 'Done' -488 btn.button_style = '' -489 btn.tooltip = '' -490 time.sleep(3) # wait a few seconds for end of data collection -491 self.data = data -492 self.timestamp = timestamp -493 self.stdev = stdev -494 self.fillpandadf() -495 # save data to html file so it is human readable and can be loaded -496 # elsewhere. -497 #self.svname = self.title + '_' + time.strftime('%y-%m-%d_%H%M%S', -498 # time.localtime()) + '.html' -499 svhtml = '<!DOCTYPE html>' \ -500 '<html><body>'+ self.defaultparamtxt + \ -501 '<table id="file_info" border="1"><tr><th>Saved as ' \ -502 '</th></tr><tr><td>' + \ -503 self.svname+'</td></tr></table>' \ -504 '<h2>DATA</h2>'+ \ -505 self.pandadf.to_html() + '</body></html>' -506 f = open(self.svname,'w') -507 f.write(svhtml) -508 f.close() -509 self.collectbtn.close() -510 del self.collectbtn -511 with self.output: -512 display(HTML( -513 '<span style="color:blue;font-weight:bold;">DATA SAVED TO:' + -514 self.svname + '</span>')) -515 return -516 -517 def fillpandadf(self): -518 datacolumns = [] -519 temptimes = np.transpose(self.timestamp) -520 tempdata = np.transpose(self.data) -521 tempstdev = np.transpose(self.stdev) -522 chncnt = 0 -523 for i in range(self.ntraces): -524 if (self.traces[i].isactive): -525 chncnt += 1 -526 for i in range(chncnt): -527 if self.ignore_skew and i > 0: -528 pass -529 else: -530 datacolumns.append(temptimes[i]) -531 datacolumns.append(tempdata[i]) -532 datacolumns.append(tempstdev[i]) -533 titles = [] -534 # Column labels. -535 chncnt = 0 -536 for i in range(self.ntraces): -537 if (self.traces[i].isactive): -538 chncnt += 1 -539 if self.ignore_skew: -540 if chncnt == 1: -541 titles.append(self.timelbl.value) -542 pass -543 else: -544 titles.append(self.traces[ -545 i].tracelbl.value + '_' + self.timelbl.value) -546 titles.append( -547 self.traces[i].tracelbl.value + '(' + self.traces[ -548 i].units.value + ')') -549 titles.append( -550 self.traces[i].tracelbl.value + '_' + 'stdev') -551 #print(str(titles)) -552 #print(str(datacolumns)) -553 self.pandadf = pd.DataFrame(np.transpose(datacolumns), columns=titles) -554 -555 def updatingplot(self, PLTconn, PLTCTL): -556 """ -557 Runs until a check of self.collectbtn.description does not return -558 'Stop Collecting'. This would probably be more efficient if set a -559 boolean. -560 Parameters -561 ---------- -562 PLTconn: Pipe -563 connection plotter end -564 DAQconn: Pipe -565 connection DAQ end -566 PLTCTL: Pipe -567 control pipe plotter end -568 DAQCTL: Pipe -569 control pipe DAQ end -570 """ -571 starttime = time.time() -572 global data -573 data = [] -574 global timestamp -575 timestamp = [] -576 global stdev -577 stdev = [] -578 datalegend = [] -579 timelegend = [] -580 stdevlegend = [] -581 whichchn = [] -582 gains = [] -583 toplotx = [] -584 toploty = [] -585 nactive = 0 -586 def convert_pkg(): -587 plttime = 0 -588 if self.ignore_skew: -589 plttime = sum(pkg[0]) / len(pkg[0]) -590 traceidx = 0 -591 tmptime = [] -592 tmpavg = [] -593 tmpstd = [] -594 tmpavg_std = [] -595 for i, k in zip(self.tracemap, self.tracefrdatachn): -596 -597 avg = pkg[1][k] -598 std = pkg[2][k] -599 avg_std = pkg[3][k] -600 avg_vdd = pkg[4][k] -601 avg, std, avg_std = self.traces[i].toselectedunits(avg, -602 std, avg_std, -603 avg_vdd) -604 avg, std, avg_std = sensors. \ -605 to_reasonable_significant_figures_fast(avg, std, avg_std) -606 tmptime.append(pkg[0][k]) -607 tmpavg.append(avg) -608 tmpstd.append(std) -609 tmpavg_std.append(avg_std) -610 if self.ignore_skew: -611 toplotx[traceidx].append(plttime) -612 else: -613 toplotx[traceidx].append(pkg[0][k]) -614 toploty[traceidx].append(avg) -615 traceidx += 1 -616 timestamp.append(tmptime) -617 data.append(tmpavg) -618 stdev.append(tmpavg_std) -619 return -620 -621 for k in self.traces: -622 if k.isactive: -623 nactive += 1 -624 if self.separate_plots: -625 self.livefig.set_subplots(rows = nactive, cols = 1, -626 shared_xaxes= True) -627 self.livefig.update_xaxes(title = self.timelbl.value, -628 row = nactive, col = 1) -629 else: -630 self.livefig.update_yaxes(title = "Values") -631 self.livefig.update_xaxes(title = self.timelbl.value) -632 active_count = 0 -633 for i in range(self.ntraces): -634 if (self.traces[i].isactive): -635 active_count += 1 -636 tempstr = self.traces[i].tracelbl.value + '(' + \ -637 self.traces[i].units.value + ')' -638 timelegend.append('time_' + tempstr) -639 datalegend.append(tempstr) -640 stdevlegend.append('stdev_' + tempstr) -641 if self.separate_plots: -642 scat = go.Scatter(y=[],x=[], name=tempstr) -643 self.livefig.add_trace(scat, row = active_count, -644 col = 1) -645 self.livefig.update_yaxes(title = self.traces[ -646 i].units.value, row = active_count, col = 1) -647 else: -648 self.livefig.add_scatter(y=[],x=[], name=tempstr) -649 toplotx.append([]) -650 toploty.append([]) -651 lastupdatetime = time.time() -652 -653 pts = 0 -654 oldpts = 0 -655 #print('about to enter while loop',end='') -656 while (self.collectbtn.description == 'Stop Collecting'): -657 #print('.',end='') -658 while PLTconn.poll(): -659 pkg = PLTconn.recv() -660 self.lastpkgstr = str(pkg) -661 #print(self.lastpkgstr) -662 # convert voltage to requested units. -663 convert_pkg() -664 currenttime = time.time() -665 mindelay = 1.0 -666 if self.separate_traces_checkbox.value: -667 mindelay = nactive*1.0 -668 else: -669 mindelay = nactive*0.5 -670 if (currenttime - lastupdatetime)>(mindelay+len(toplotx[0])*len( -671 toplotx)/1000): -672 lastupdatetime = currenttime -673 for k in range(len(self.livefig.data)): -674 self.livefig.data[k].x=toplotx[k] -675 self.livefig.data[k].y=toploty[k] -676 #time.sleep(1) -677 PLTCTL.send('send') -678 time.sleep(self.delta) -679 # print ('btn.description='+str(btn.description)) -680 endtime = time.time() -681 PLTCTL.send('stop') -682 time.sleep(0.5) # wait 0.5 second to collect remaining data -683 PLTCTL.send('send') -684 time.sleep(0.5) -685 msg = '' -686 while (msg != 'done'): -687 while PLTconn.poll(): -688 pkg = PLTconn.recv() -689 # print(str(pkg)) -690 # convert voltage to requested units. -691 convert_pkg() -692 PLTCTL.send('send') -693 time.sleep(0.2) -694 if PLTCTL.poll(): -695 msg = PLTCTL.recv() -696 # print (str(msg)) -697 if (msg != 'done'): -698 print('Received unexpected message: ' + str(msg)) -699 for k in range(len(self.livefig.data)): -700 self.livefig.data[k].x = toplotx[k] -701 self.livefig.data[k].y = toploty[k] -702 -703# TODO delete newRun once sure not needed. -704# def newRun(livefig): -705# """ -706# Set up a new data collection run and add it to the list of runs. -707# """ -708# nrun = len(runs) + 1 -709# runs.append(DAQinstance(nrun, livefig, title='Run-' + str(nrun))) -710# runs[nrun - 1].setup() -711# pass -712 -713def Run(name): -714 """Load a run from stored data or start a new run if the local file for -715 the run does not exist. -716 Parameters -717 ---------- -718 name: str -719 String name for the run. The data will be stored in a file of this -720 name with the extension of `.jpidaq.html`. -721 """ -722 from pathlib import Path -723 from IPython import get_ipython -724 from IPython.display import display -725 global_dict = get_ipython().user_ns -726 runs = None -727 if 'runs' in global_dict and 'DAQinstance' in global_dict: -728 runs = global_dict['runs'] -729 else: -730 return ('Initialization of JupyterPiDAQ required') -731 # Check if run completed, if so reload data, display and exit -732 datafilepath = Path.cwd() / Path(str(name) + '.jpidaq.html') -733 if datafilepath.exists(): -734 # display the data as a live plotly plot. -735 svname = name + '.jpidaq.html' -736 runs.append(DAQinstance(len(runs)+1, title = name)) -737 runs[-1]._load_from_html(svname) -738 display(HTML(runs[-1].defaultparamtxt)) -739 display(HTML('<h3>Saved as: '+runs[-1].svname+'</h3>')) -740 display(runs[-1].livefig) -741 display(HTML(runs[-1].defaultcollecttxt)) -742 return -743 nrun = len(runs) + 1 -744 runs.append(DAQinstance(nrun, title=name)) -745 runs[-1].setup() -746 return -747 -748def doRun(whichrun): -749 with whichrun.output: -750 display(HTML('<span id="LiveRun_'+str(whichrun.idno)+'"></span>')) -751 display(HTML(whichrun.defaultparamtxt)) -752 if hasattr(whichrun, "collectbtn"): -753 # only show if hasn't already collected data -754 whichrun.collectbtn.on_click(whichrun.collectclick) -755 display(whichrun.collectbtn) -756 display(HTML(whichrun.defaultcollecttxt)) -757 pass -758 -759# TODO delete displayRun once not needed. -760# def displayRun(runidx,file): -761# """ -762# Displays a run. It can fall back to loading from a file if the outputarea -763# is accidentally cleared. -764# :param runidx: index+1 for the run in the runs array. Thus, the run id #. -765# :param file: name of the file the run is saved to -766# :return: A string warning if things are not initialized properly. -767# """ -768# from IPython import get_ipython -769# from JPSLUtils import find_pandas_dataframe_names, find_figure_names -770# idxnum = runidx - 1 -771# run_id_table = pd.read_html(file, attrs={'id': 'run_id'})[0] -772# run_title = run_id_table['Title'][0] -773# run_id = run_id_table['Id #'][0] -774# svname = pd.read_html(file, attrs={'id': 'file_info'})[0]['Saved as'][0] -775# global_dict = get_ipython().user_ns -776# runs = None -777# if 'runs' in global_dict and 'DAQinstance' in global_dict: -778# runs = global_dict['runs'] -779# else: -780# return ('Initialization of JupyterPiDAQ required') -781# exists = None -782# if len(runs)>=runidx: -783# if isinstance(runs[idxnum].livefig,go.FigureWidget) and runs[ -784# idxnum].idno == run_id and runs[idxnum].svname ==svname: -785# exists = True -786# else: -787# exists = False -788# if exists: -789# display(HTML(runs[idxnum].defaultparamtxt)) -790# display(HTML('<h3>Saved as: '+runs[idxnum].svname+'</h3>')) -791# runs[idxnum].livefig.show() -792# display(HTML(runs[idxnum].defaultcollecttxt)) -793# JPSLUtils.select_containing_cell("LiveRun_"+str(runidx)) -794# JPSLUtils.delete_selected_cell() -795# else: -796# # Fall back on loading the data from the default save file. -797# # Note: the file must be available. -798# nrunfigs = 0 -799# for k in find_figure_names(): -800# if k.startswith('run_fig'): -801# nrunfigs+=1 -802# runfigname = 'run_fig'+str(nrunfigs+1) -803# global_dict[runfigname] = go.FigureWidget() -804# fig = global_dict[runfigname] -805# runs.append(DAQinstance(run_id, fig, title=run_title)) -806# idxnum = len(runs)-1 -807# runs[idxnum]._load_from_html(file) -808# display(HTML(runs[idxnum].defaultparamtxt)) -809# display(HTML('<h3>Saved as: ' + runs[idxnum].svname + '</h3>')) -810# runs[idxnum].livefig.show() -811# display(HTML(runs[idxnum].defaultcollecttxt)) -812# # protect the cell -813# JPSLUtils.OTJS('protect_selected_cells();') -814# pass -815 -816def update_runsdrp(): -817 # get list of runs -818 runlst = [('Choose Run', -1)] -819 for i in range(len(runs)): -820 runlst.append((str(i + 1) + ': ' + runs[i].title, i)) -821 # buid selection menu -822 global runsdrp -823 runsdrp = widgets.Dropdown( -824 options=runlst, -825 value=-1, -826 description='Select Run #:', -827 disabled=False, -828 ) -829 pass -830 -831def showSelectedRunTable(change): -832 global runsdrp -833 global last_run_table_out -834 whichrun = runsdrp.value -835 runsdrp.close() -836 last_run_table_out.clear_output() -837 tbldiv = '<div style="height:10em;">' + str(runs[whichrun].title) -838 tbldiv += str(runs[whichrun].pandadf.to_html()) + '</div>' -839 with last_run_table_out: -840 display(HTML(tbldiv)) -841 return -842 -843def showDataTable(): -844 """ -845 Provides a menu to select which run. Then displays the run in a -846 10 em high scrolling table. Selection menu is removed after choice -847 is made. -848 """ -849 from ipywidgets import Output -850 global last_run_table_out -851 last_run_table_out = Output() -852 update_runsdrp() -853 global runsdrp -854 runsdrp.observe(showSelectedRunTable, names='value') -855 with last_run_table_out: -856 display(runsdrp) -857 display(last_run_table_out) -858 # will display selected run and delete menu upon selection. -859 return -860def newCalculatedColumn(): -861 """ -862 Uses jupyter-pandas-GUI.new_pandas_column_GUI to provide a GUI expression -863 composer. This method finds the datasets and launches the GUI. -864 """ -865 df_info = [] -866 for i in range(len(runs)): -867 df_info.append([runs[i].pandadf, 'runs['+str(i)+'].pandadf', -868 str(runs[i].title)]) -869 new_pandas_column_GUI(df_info) -870 pass -871 -872def newPlot(): -873 """ -874 Uses jupyter-pandas-GUI.plot_pandas_GUI to provide a GUI expression -875 composer. This method finds the datasets and launches the GUI. -876 """ -877 df_info = [] -878 for i in range(len(runs)): -879 if isinstance(runs[i].pandadf,pd.DataFrame): -880 df_info.append([runs[i].pandadf, 'runs['+str(i)+'].pandadf', -881 str(runs[i].title)]) -882 plot_pandas_GUI(df_info) -883 pass -884 -885def newFit(): -886 """ -887 Uses jupyter-pandas-GUI.fit_pandas_GUI to provide a GUI expression -888 composer. This method finds the datasets and launches the GUI. -889 """ -890 df_info = [] -891 for i in range(len(runs)): -892 if isinstance(runs[i].pandadf,pd.DataFrame): -893 df_info.append([runs[i].pandadf, 'runs['+str(i)+'].pandadf', -894 str(runs[i].title)]) -895 fit_pandas_GUI(df_info) -896 pass +429 display(runs[self.idno - 1].livefig) +430 pass +431 +432 def setup(self): +433 with self.output: +434 display(HTML("<h3 id ='RunSetUp' " +435 "style='text-align:center;'>Set Run Parameters</h3>")) +436 self.setupbtn.on_click(self.setupclick) +437 display(self.runtitle) +438 for i in range(self.ntraces): +439 self.traces[i].setup() +440 display(self.setup_layout) +441 display(self.output) +442 pass +443 +444 def collectclick(self, btn): +445 if (btn.description == 'Start Collecting'): +446 btn.description = 'Stop Collecting' +447 btn.button_style = 'danger' +448 btn.tooltip = 'Stop the data collection' +449 # do not allow parameters to be reset after starting run. +450 self.setupbtn.disabled = True +451 self.setupbtn.tooltip = 'Parameters locked. The run has started.' +452 self.rateinp.disabled = True +453 self.timelbl.disabled = True +454 nactive = 0 +455 for k in self.traces: +456 if k.isactive: +457 nactive += 1 +458 whichchn = [] +459 gains =[] +460 for i in range(self.ntraces): +461 if (self.traces[i].isactive): +462 brd = self.traces[i].board +463 chn = self.traces[i].channel +464 newchn = True +465 if len(whichchn) > 0: +466 for k in range(len(whichchn)): +467 if whichchn[k]['board'] == brd \ +468 and whichchn[k]['chnl'] == chn: +469 self.tracefrdatachn.append(k) +470 newchn = False +471 if newchn: +472 whichchn.append({'board': brd, +473 'chnl': chn}) +474 gains.append(self.traces[i].toselectedgain) +475 self.tracefrdatachn.append(len(whichchn)-1) +476 # Use up to 30% of the time for averaging if channels were spaced +477 # evenly between data collection times (with DACQ2 they appear +478 # more synchronous than that). +479 self.averaging_time = self.delta / nactive / 3 +480 DAQ = Process(target=DAQProc, +481 args=( +482 whichchn, gains, self.averaging_time, self.delta, +483 self.DAQconn, self.DAQCTL)) +484 DAQ.start() +485 self.pltthread.start() +486 # self.updatingplot() hangs up user interface +487 else: +488 btn.description = 'Done' +489 btn.button_style = '' +490 btn.tooltip = '' +491 # wait a plotting thread to terminate +492 self.pltthread.join() +493 self.data = data +494 self.timestamp = timestamp +495 self.stdev = stdev +496 self.fillpandadf() +497 # save data to html file so it is human readable and can be loaded +498 # elsewhere. +499 #self.svname = self.title + '_' + time.strftime('%y-%m-%d_%H%M%S', +500 # time.localtime()) + '.html' +501 svhtml = '<!DOCTYPE html>' \ +502 '<html><body>'+ self.defaultparamtxt + \ +503 '<table id="file_info" border="1"><tr><th>Saved as ' \ +504 '</th></tr><tr><td>' + \ +505 self.svname+'</td></tr></table>' \ +506 '<h2>DATA</h2>'+ \ +507 self.pandadf.to_html() + '</body></html>' +508 f = open(self.svname,'w') +509 f.write(svhtml) +510 f.close() +511 self.collectbtn.close() +512 del self.collectbtn +513 with self.output: +514 display(HTML( +515 '<span style="color:blue;font-weight:bold;">DATA SAVED TO:' + +516 self.svname + '</span>')) +517 return +518 +519 def fillpandadf(self): +520 datacolumns = [] +521 temptimes = np.transpose(self.timestamp) +522 tempdata = np.transpose(self.data) +523 tempstdev = np.transpose(self.stdev) +524 chncnt = 0 +525 for i in range(self.ntraces): +526 if (self.traces[i].isactive): +527 chncnt += 1 +528 for i in range(chncnt): +529 if self.ignore_skew and i > 0: +530 pass +531 else: +532 datacolumns.append(temptimes[i]) +533 datacolumns.append(tempdata[i]) +534 datacolumns.append(tempstdev[i]) +535 titles = [] +536 # Column labels. +537 chncnt = 0 +538 for i in range(self.ntraces): +539 if (self.traces[i].isactive): +540 chncnt += 1 +541 if self.ignore_skew: +542 if chncnt == 1: +543 titles.append(self.timelbl.value) +544 pass +545 else: +546 titles.append(self.traces[ +547 i].tracelbl.value + '_' + self.timelbl.value) +548 titles.append( +549 self.traces[i].tracelbl.value + '(' + self.traces[ +550 i].units.value + ')') +551 titles.append( +552 self.traces[i].tracelbl.value + '_' + 'stdev') +553 #print(str(titles)) +554 #print(str(datacolumns)) +555 self.pandadf = pd.DataFrame(np.transpose(datacolumns), columns=titles) +556 +557 def updatingplot(self, PLTconn, PLTCTL): +558 """ +559 Runs until a check of self.collectbtn.description does not return +560 'Stop Collecting'. This would probably be more efficient if set a +561 boolean. +562 Parameters +563 ---------- +564 PLTconn: Pipe +565 connection plotter end +566 DAQconn: Pipe +567 connection DAQ end +568 PLTCTL: Pipe +569 control pipe plotter end +570 DAQCTL: Pipe +571 control pipe DAQ end +572 """ +573 starttime = time.time() +574 global data +575 data = [] +576 global timestamp +577 timestamp = [] +578 global stdev +579 stdev = [] +580 datalegend = [] +581 timelegend = [] +582 stdevlegend = [] +583 whichchn = [] +584 gains = [] +585 toplotx = [] +586 toploty = [] +587 nactive = 0 +588 def convert_pkg(): +589 plttime = 0 +590 if self.ignore_skew: +591 plttime = sum(pkg[0]) / len(pkg[0]) +592 traceidx = 0 +593 tmptime = [] +594 tmpavg = [] +595 tmpstd = [] +596 tmpavg_std = [] +597 for i, k in zip(self.tracemap, self.tracefrdatachn): +598 +599 avg = pkg[1][k] +600 std = pkg[2][k] +601 avg_std = pkg[3][k] +602 avg_vdd = pkg[4][k] +603 avg, std, avg_std = self.traces[i].toselectedunits(avg, +604 std, avg_std, +605 avg_vdd) +606 avg, std, avg_std = sensors. \ +607 to_reasonable_significant_figures_fast(avg, std, avg_std) +608 tmptime.append(pkg[0][k]) +609 tmpavg.append(avg) +610 tmpstd.append(std) +611 tmpavg_std.append(avg_std) +612 if self.ignore_skew: +613 toplotx[traceidx].append(plttime) +614 else: +615 toplotx[traceidx].append(pkg[0][k]) +616 toploty[traceidx].append(avg) +617 traceidx += 1 +618 timestamp.append(tmptime) +619 data.append(tmpavg) +620 stdev.append(tmpavg_std) +621 return +622 +623 for k in self.traces: +624 if k.isactive: +625 nactive += 1 +626 if self.separate_plots: +627 self.livefig.set_subplots(rows = nactive, cols = 1, +628 shared_xaxes= True) +629 self.livefig.update_xaxes(title = self.timelbl.value, +630 row = nactive, col = 1) +631 else: +632 self.livefig.update_yaxes(title = "Values") +633 self.livefig.update_xaxes(title = self.timelbl.value) +634 active_count = 0 +635 for i in range(self.ntraces): +636 if (self.traces[i].isactive): +637 active_count += 1 +638 tempstr = self.traces[i].tracelbl.value + '(' + \ +639 self.traces[i].units.value + ')' +640 timelegend.append('time_' + tempstr) +641 datalegend.append(tempstr) +642 stdevlegend.append('stdev_' + tempstr) +643 if self.separate_plots: +644 scat = go.Scatter(y=[],x=[], name=tempstr) +645 self.livefig.add_trace(scat, row = active_count, +646 col = 1) +647 self.livefig.update_yaxes(title = self.traces[ +648 i].units.value, row = active_count, col = 1) +649 else: +650 self.livefig.add_scatter(y=[],x=[], name=tempstr) +651 toplotx.append([]) +652 toploty.append([]) +653 lastupdatetime = time.time() +654 +655 pts = 0 +656 oldpts = 0 +657 #print('about to enter while loop',end='') +658 while (self.collectbtn.description == 'Stop Collecting'): +659 #print('.',end='') +660 while PLTconn.poll(): +661 pkg = PLTconn.recv() +662 self.lastpkgstr = str(pkg) +663 #print(self.lastpkgstr) +664 # convert voltage to requested units. +665 convert_pkg() +666 currenttime = time.time() +667 mindelay = 1.0 +668 if self.separate_traces_checkbox.value: +669 mindelay = nactive*1.0 +670 else: +671 mindelay = nactive*0.5 +672 if (currenttime - lastupdatetime)>(mindelay+len(toplotx[0])*len( +673 toplotx)/1000): +674 lastupdatetime = currenttime +675 for k in range(len(self.livefig.data)): +676 self.livefig.data[k].x=toplotx[k] +677 self.livefig.data[k].y=toploty[k] +678 #time.sleep(1) +679 PLTCTL.send('send') +680 time.sleep(self.delta) +681 # print ('btn.description='+str(btn.description)) +682 endtime = time.time() +683 PLTCTL.send('stop') +684 time.sleep(0.5) # wait 0.5 second to collect remaining data +685 PLTCTL.send('send') +686 time.sleep(0.5) +687 msg = '' +688 while (msg != 'done'): +689 while PLTconn.poll(): +690 pkg = PLTconn.recv() +691 # print(str(pkg)) +692 # convert voltage to requested units. +693 convert_pkg() +694 PLTCTL.send('send') +695 time.sleep(0.2) +696 if PLTCTL.poll(): +697 msg = PLTCTL.recv() +698 # print (str(msg)) +699 if (msg != 'done'): +700 print('Received unexpected message: ' + str(msg)) +701 for k in range(len(self.livefig.data)): +702 self.livefig.data[k].x = toplotx[k] +703 self.livefig.data[k].y = toploty[k] +704 return +705 +706# TODO delete newRun once sure not needed. +707# def newRun(livefig): +708# """ +709# Set up a new data collection run and add it to the list of runs. +710# """ +711# nrun = len(runs) + 1 +712# runs.append(DAQinstance(nrun, livefig, title='Run-' + str(nrun))) +713# runs[nrun - 1].setup() +714# pass +715 +716def Run(name): +717 """Load a run from stored data or start a new run if the local file for +718 the run does not exist. +719 Parameters +720 ---------- +721 name: str +722 String name for the run. The data will be stored in a file of this +723 name with the extension of `.jpidaq.html`. +724 """ +725 from pathlib import Path +726 from IPython import get_ipython +727 from IPython.display import display +728 global_dict = get_ipython().user_ns +729 runs = None +730 if 'runs' in global_dict and 'DAQinstance' in global_dict: +731 runs = global_dict['runs'] +732 else: +733 return ('Initialization of JupyterPiDAQ required') +734 # Check if run completed, if so reload data, display and exit +735 datafilepath = Path.cwd() / Path(str(name) + '.jpidaq.html') +736 if datafilepath.exists(): +737 # display the data as a live plotly plot. +738 svname = name + '.jpidaq.html' +739 runs.append(DAQinstance(len(runs)+1, title = name)) +740 runs[-1]._load_from_html(svname) +741 display(HTML(runs[-1].defaultparamtxt)) +742 display(HTML('<h3>Saved as: '+runs[-1].svname+'</h3>')) +743 display(runs[-1].livefig) +744 display(HTML(runs[-1].defaultcollecttxt)) +745 return +746 nrun = len(runs) + 1 +747 runs.append(DAQinstance(nrun, title=name)) +748 runs[-1].setup() +749 return +750 +751def doRun(whichrun): +752 with whichrun.output: +753 display(HTML('<span id="LiveRun_'+str(whichrun.idno)+'"></span>')) +754 display(HTML(whichrun.defaultparamtxt)) +755 if hasattr(whichrun, "collectbtn"): +756 # only show if hasn't already collected data +757 whichrun.collectbtn.on_click(whichrun.collectclick) +758 display(whichrun.collectbtn) +759 display(HTML(whichrun.defaultcollecttxt)) +760 pass +761 +762# TODO delete displayRun once not needed. +763# def displayRun(runidx,file): +764# """ +765# Displays a run. It can fall back to loading from a file if the outputarea +766# is accidentally cleared. +767# :param runidx: index+1 for the run in the runs array. Thus, the run id #. +768# :param file: name of the file the run is saved to +769# :return: A string warning if things are not initialized properly. +770# """ +771# from IPython import get_ipython +772# from JPSLUtils import find_pandas_dataframe_names, find_figure_names +773# idxnum = runidx - 1 +774# run_id_table = pd.read_html(file, attrs={'id': 'run_id'})[0] +775# run_title = run_id_table['Title'][0] +776# run_id = run_id_table['Id #'][0] +777# svname = pd.read_html(file, attrs={'id': 'file_info'})[0]['Saved as'][0] +778# global_dict = get_ipython().user_ns +779# runs = None +780# if 'runs' in global_dict and 'DAQinstance' in global_dict: +781# runs = global_dict['runs'] +782# else: +783# return ('Initialization of JupyterPiDAQ required') +784# exists = None +785# if len(runs)>=runidx: +786# if isinstance(runs[idxnum].livefig,go.FigureWidget) and runs[ +787# idxnum].idno == run_id and runs[idxnum].svname ==svname: +788# exists = True +789# else: +790# exists = False +791# if exists: +792# display(HTML(runs[idxnum].defaultparamtxt)) +793# display(HTML('<h3>Saved as: '+runs[idxnum].svname+'</h3>')) +794# runs[idxnum].livefig.show() +795# display(HTML(runs[idxnum].defaultcollecttxt)) +796# JPSLUtils.select_containing_cell("LiveRun_"+str(runidx)) +797# JPSLUtils.delete_selected_cell() +798# else: +799# # Fall back on loading the data from the default save file. +800# # Note: the file must be available. +801# nrunfigs = 0 +802# for k in find_figure_names(): +803# if k.startswith('run_fig'): +804# nrunfigs+=1 +805# runfigname = 'run_fig'+str(nrunfigs+1) +806# global_dict[runfigname] = go.FigureWidget() +807# fig = global_dict[runfigname] +808# runs.append(DAQinstance(run_id, fig, title=run_title)) +809# idxnum = len(runs)-1 +810# runs[idxnum]._load_from_html(file) +811# display(HTML(runs[idxnum].defaultparamtxt)) +812# display(HTML('<h3>Saved as: ' + runs[idxnum].svname + '</h3>')) +813# runs[idxnum].livefig.show() +814# display(HTML(runs[idxnum].defaultcollecttxt)) +815# # protect the cell +816# JPSLUtils.OTJS('protect_selected_cells();') +817# pass +818 +819def update_runsdrp(): +820 # get list of runs +821 runlst = [('Choose Run', -1)] +822 for i in range(len(runs)): +823 runlst.append((str(i + 1) + ': ' + runs[i].title, i)) +824 # buid selection menu +825 global runsdrp +826 runsdrp = widgets.Dropdown( +827 options=runlst, +828 value=-1, +829 description='Select Run #:', +830 disabled=False, +831 ) +832 pass +833 +834def showSelectedRunTable(change): +835 global runsdrp +836 global last_run_table_out +837 whichrun = runsdrp.value +838 runsdrp.close() +839 last_run_table_out.clear_output() +840 tbldiv = '<div style="height:10em;">' + str(runs[whichrun].title) +841 tbldiv += str(runs[whichrun].pandadf.to_html()) + '</div>' +842 with last_run_table_out: +843 display(HTML(tbldiv)) +844 return +845 +846def showDataTable(): +847 """ +848 Provides a menu to select which run. Then displays the run in a +849 10 em high scrolling table. Selection menu is removed after choice +850 is made. +851 """ +852 from ipywidgets import Output +853 global last_run_table_out +854 last_run_table_out = Output() +855 update_runsdrp() +856 global runsdrp +857 runsdrp.observe(showSelectedRunTable, names='value') +858 with last_run_table_out: +859 display(runsdrp) +860 display(last_run_table_out) +861 # will display selected run and delete menu upon selection. +862 return +863def newCalculatedColumn(): +864 """ +865 Uses jupyter-pandas-GUI.new_pandas_column_GUI to provide a GUI expression +866 composer. This method finds the datasets and launches the GUI. +867 """ +868 df_info = [] +869 for i in range(len(runs)): +870 df_info.append([runs[i].pandadf, 'runs['+str(i)+'].pandadf', +871 str(runs[i].title)]) +872 new_pandas_column_GUI(df_info) +873 pass +874 +875def newPlot(): +876 """ +877 Uses jupyter-pandas-GUI.plot_pandas_GUI to provide a GUI expression +878 composer. This method finds the datasets and launches the GUI. +879 """ +880 df_info = [] +881 for i in range(len(runs)): +882 if isinstance(runs[i].pandadf,pd.DataFrame): +883 df_info.append([runs[i].pandadf, 'runs['+str(i)+'].pandadf', +884 str(runs[i].title)]) +885 plot_pandas_GUI(df_info) +886 pass +887 +888def newFit(): +889 """ +890 Uses jupyter-pandas-GUI.fit_pandas_GUI to provide a GUI expression +891 composer. This method finds the datasets and launches the GUI. +892 """ +893 df_info = [] +894 for i in range(len(runs)): +895 if isinstance(runs[i].pandadf,pd.DataFrame): +896 df_info.append([runs[i].pandadf, 'runs['+str(i)+'].pandadf', +897 str(runs[i].title)]) +898 fit_pandas_GUI(df_info) +899 pass @@ -1012,583 +1015,585 @@

          -
          126class DAQinstance():
          -127    def __init__(self, idno, title='None', ntraces=4, **kwargs):
          -128        """
          -129        Data Aquistion Instance (a run).
          -130
          -131        :param idno : id number you wish to use to keep track
          -132        :param title: optional name
          -133        :param ntraces: number of traces (default = 4) more than 4 easily
          -134            overwhelms a pi4.
          -135        :param kwargs:
          -136            :ignore_skew: bool (default: True) if True only a single average
          -137            collection time will be recorded for each time in a multichannel
          -138            data collection. If False a separate set of time will be
          -139            recorded for each channel.
          -140        """
          -141        from plotly import graph_objects as go
          -142        self.ignore_skew = kwargs.pop('ignore_skew',True)
          -143        self.idno = idno
          -144        self.livefig = go.FigureWidget(layout_template='simple_white')
          -145        self.title = str(title)
          -146        self.svname = title + '.jpidaq.html'
          -147        self.averaging_time = 0.1  # seconds adjusted based on collection rate
          -148        self.gain = [1] * ntraces
          -149        self.data = []
          -150        self.timestamp = []
          -151        self.stdev = []
          -152        self.pandadf = None
          -153        self.ntraces = ntraces
          -154        self.separate_plots = True
          -155        self.traces = []
          -156        # index map from returned data to trace,
          -157        self.tracemap = []
          -158        self.tracefrdatachn = []
          -159        self.tracelbls = []
          -160        self.units = []
          -161        for i in range(self.ntraces):
          -162            self.traces.append(ChannelSettings(i, availboards))
          -163        self.ratemax = 20.0  # Hz
          -164        self.rate = 1.0  # Hz
          -165        self.deltamin = 1 / self.ratemax
          -166        self.delta = 1.0 / self.rate
          -167        self.setupbtn = widgets.Button(
          -168            description='Set Parameters',
          -169            disabled=False,
          -170            button_style='info',
          -171            # 'success', 'info', 'warning', 'danger' or ''
          -172            tooltip='Click to set collection parameters to displayed values.',
          -173            icon='')
          -174        self.collectbtn = widgets.Button(
          -175            description='Start Collecting',
          -176            disabled=False,
          -177            button_style='success',
          -178            # 'success', 'info', 'warning', 'danger' or ''
          -179            tooltip='Start collecting data and plotting it. '
          -180                    'Will make new graph.',
          -181            icon='')
          -182        self.separate_traces_checkbox = widgets.Checkbox(
          -183            value = self.separate_plots,
          -184            description = 'Display multiple traces as stacked graphs. ' \
          -185                        'Uncheck to display all on the same graph.',
          -186            layout = widgets.Layout(width='80%'),
          -187            disabled = False)
          -188        self.rateinp = widgets.BoundedFloatText(
          -189            value=self.rate,
          -190            min=0,
          -191            max=self.ratemax,
          -192            step=self.ratemax / 1000.0,
          -193            description='Rate (Hz):',
          -194            disabled=False)
          -195        self.timelbl = widgets.Text(
          -196            value='Time(s)',
          -197            placeholder='Type something',
          -198            description='X-axis label (time):',
          +            
          127class DAQinstance():
          +128    def __init__(self, idno, title='None', ntraces=4, **kwargs):
          +129        """
          +130        Data Aquistion Instance (a run).
          +131
          +132        :param idno : id number you wish to use to keep track
          +133        :param title: optional name
          +134        :param ntraces: number of traces (default = 4) more than 4 easily
          +135            overwhelms a pi4.
          +136        :param kwargs:
          +137            :ignore_skew: bool (default: True) if True only a single average
          +138            collection time will be recorded for each time in a multichannel
          +139            data collection. If False a separate set of time will be
          +140            recorded for each channel.
          +141        """
          +142        from plotly import graph_objects as go
          +143        self.ignore_skew = kwargs.pop('ignore_skew',True)
          +144        self.idno = idno
          +145        self.livefig = go.FigureWidget(layout_template='simple_white')
          +146        self.PLTconn, self.DAQconn = Pipe()
          +147        self.DAQCTL, self.PLTCTL = Pipe()
          +148        self.pltthread = threading.Thread(target=self.updatingplot, args=(
          +149                                        self.PLTconn, self.PLTCTL))
          +150        self.title = str(title)
          +151        self.svname = title + '.jpidaq.html'
          +152        self.averaging_time = 0.1  # seconds adjusted based on collection rate
          +153        self.gain = [1] * ntraces
          +154        self.data = []
          +155        self.timestamp = []
          +156        self.stdev = []
          +157        self.pandadf = None
          +158        self.ntraces = ntraces
          +159        self.separate_plots = True
          +160        self.traces = []
          +161        # index map from returned data to trace,
          +162        self.tracemap = []
          +163        self.tracefrdatachn = []
          +164        self.tracelbls = []
          +165        self.units = []
          +166        for i in range(self.ntraces):
          +167            self.traces.append(ChannelSettings(i, availboards))
          +168        self.ratemax = 20.0  # Hz
          +169        self.rate = 1.0  # Hz
          +170        self.deltamin = 1 / self.ratemax
          +171        self.delta = 1.0 / self.rate
          +172        self.setupbtn = widgets.Button(
          +173            description='Set Parameters',
          +174            disabled=False,
          +175            button_style='info',
          +176            # 'success', 'info', 'warning', 'danger' or ''
          +177            tooltip='Click to set collection parameters to displayed values.',
          +178            icon='')
          +179        self.collectbtn = widgets.Button(
          +180            description='Start Collecting',
          +181            disabled=False,
          +182            button_style='success',
          +183            # 'success', 'info', 'warning', 'danger' or ''
          +184            tooltip='Start collecting data and plotting it. '
          +185                    'Will make new graph.',
          +186            icon='')
          +187        self.separate_traces_checkbox = widgets.Checkbox(
          +188            value = self.separate_plots,
          +189            description = 'Display multiple traces as stacked graphs. ' \
          +190                        'Uncheck to display all on the same graph.',
          +191            layout = widgets.Layout(width='80%'),
          +192            disabled = False)
          +193        self.rateinp = widgets.BoundedFloatText(
          +194            value=self.rate,
          +195            min=0,
          +196            max=self.ratemax,
          +197            step=self.ratemax / 1000.0,
          +198            description='Rate (Hz):',
           199            disabled=False)
          -200        self.runtitle = widgets.Text(
          -201            value=self.title,
          -202            placeholder='Type title/description',
          -203            description='Run title',
          +200        self.timelbl = widgets.Text(
          +201            value='Time(s)',
          +202            placeholder='Type something',
          +203            description='X-axis label (time):',
           204            disabled=False)
          -205        self.defaultparamtxt = ''
          -206        self.defaultcollecttxt = '<span style="color:blue;"> To accurately '
          -207        self.defaultcollecttxt += 'read point location on graph you can zoom '
          -208        self.defaultcollecttxt += 'in. Drag to zoom. Additional controls show '
          -209        self.defaultcollecttxt += 'above plot when you hover over it.</span>'
          -210        self.collecttxt = widgets.HTML(
          -211            value=self.defaultcollecttxt,
          -212            placeholder='',
          -213            description='')
          -214        self.setup_layout_bottom = widgets.HBox(
          -215            [self.rateinp, self.timelbl, self.setupbtn])
          -216        self.setup_layout = widgets.VBox([self.separate_traces_checkbox,
          -217                                          self.setup_layout_bottom])
          -218        self.collect_layout = widgets.HBox([self.collectbtn, self.collecttxt])
          -219        self.output = widgets.Output()
          -220    def _make_defaultparamtxt(self):
          -221        """
          -222        Uses AdvancedHTMLParser (mimics javascript) to generate valid HTML for
          -223        the default parameter text.
          -224        :return: valid html string for the default parameter text.
          -225        """
          -226        from AdvancedHTMLParser import AdvancedTag as domel
          -227        run_info=domel('div')
          -228        run_info.setAttribute('id','DAQRun_' + str(self.idno) + '_info')
          -229        run_info.setAttribute('class','run_info')
          -230        run_id = domel('table')
          -231        run_id.setAttribute('id','run_id')
          -232        run_id.setAttribute('border', '1')
          -233        tr = domel('tr')
          -234        tr.appendInnerHTML('<th>Title</th><th>Id #</th>')
          -235        run_id.appendChild(tr)
          -236        tr = domel('tr')
          -237        tr.appendInnerHTML('<td>' + str(self.title) + '</td>' \
          -238                            '<td>' + str(self.idno) + '</td>')
          -239        run_id.appendChild(tr)
          -240        run_info.appendChild(run_id)
          -241        # table of run parameters
          -242        run_param = domel('table')
          -243        run_param.setAttribute('border','1')
          -244        run_param.setAttribute('id','run_param')
          -245        tr = domel('tr')
          -246        tr.setAttribute('style','text-align:center;')
          -247        tr.appendInnerHTML('<th>Approx. Rate (Hz)</th>' \
          -248                                '<th>Approx. Delta (s)</th>' \
          -249                                '<th>X-label </th>' \
          -250                                '<th>X-cols</th>' \
          -251                                '<th>Y-cols</th>' \
          -252                                '<th>err-cols<sup ' \
          -253                                'style="color:blue;">a</sup></th>' \
          -254                                '<th>One Plot</th>' )
          -255        run_param.appendChild(tr)
          -256        run_info.appendChild(run_param)
          -257        tr = domel('tr')
          -258        tr.setAttribute('style','text-align:center;')
          -259        tr.appendInnerHTML('<td>' + str(self.rate) + '</td>' \
          -260                            '<td>' + str(self.delta) + '</td>' \
          -261                            '<td>' + self.timelbl.value + '</td>')
          -262        xlist = '['
          -263        ylist = '['
          -264        errlist = '['
          -265        if self.ignore_skew:
          -266            xlist += '0]'
          -267            tempcount = 0
          -268            for k in self.traces:
          -269                if k.isactive:
          -270                    ylist += str(2 * tempcount + 1) + ','
          -271                    errlist += str(2 * tempcount + 2) + ','
          -272                    tempcount += 1
          -273            ylist = ylist[:-1] + ']'
          -274            errlist = errlist[:-1] + ']'
          -275        else:
          -276            tempcount = 0
          -277            for k in self.traces:
          -278                if k.isactive:
          -279                    xlist += str(3 * tempcount) + ','
          -280                    ylist += str(3 * tempcount + 1) + ','
          -281                    errlist += str(3 * tempcount + 2) + ','
          -282                    tempcount += 1
          -283            xlist = xlist[:-1] + ']'
          -284            ylist = ylist[:-1] + ']'
          -285            errlist = errlist[:-1] + ']'
          -286        tr.appendInnerHTML('<td>' + xlist + '</td><td>' + ylist + '</td>')
          -287        td = domel('td')
          -288        td.appendText(errlist)
          -289        tr.appendChild(td)
          -290        td = domel('td')
          -291        td.appendText(str(not (self.separate_plots)))
          -292        tr.appendChild(td)
          -293        run_param.appendChild(tr)
          -294        footer = domel('tfoot')
          -295        tr = domel('tr')
          -296        td = domel('td')
          -297        td.setAttribute('colspan','7')
          -298        td.appendInnerHTML('<sup style="color:blue;">a</sup>The ' \
          -299                            'standard deviation of the number in the ' \
          -300                            'column immediately to the left based on the ' \
          -301                            'variation in signal during the averaging time ' \
          -302                            'for the data point.')
          -303        tr.appendChild(td)
          -304        footer.appendChild(tr)
          -305        run_param.appendChild(footer)
          -306        # table of trace information
          -307        traceinfo = domel('table')
          -308        traceinfo.setAttribute('class','traceinfo')
          -309        traceinfo.setAttribute('id','traceinfo')
          -310        traceinfo.setAttribute('border','1')
          -311        tr = domel('tr')
          -312        tr.setAttribute('style','text-align:center;')
          -313        tr.appendInnerHTML('<th>Trace #</th><th>Title</th><th>Units</th>' \
          -314                            '<th>Board</th><th>Channel</th><th>Sensor</th>' \
          -315                            '<th>Gain</th>')
          -316        traceinfo.appendChild(tr)
          -317        for i in range(self.ntraces):
          -318            if (self.traces[i].isactive):
          -319                self.tracemap.append(i)
          -320                tr = domel('tr')
          -321                tr.setAttribute('style', 'text-align:center;')
          -322                tr.appendInnerHTML('<td>' + str(i) + '</td>' \
          -323                            '<td>' + self.traces[i].tracelbl.value + '</td>' \
          -324                            '<td>' + self.traces[i].units.value + '</td>' \
          -325                            '<td>' + str(self.traces[i].boardchoice.value) + \
          -326                            ' ' + self.traces[i].board.name + '</td>' \
          -327                            '<td>' + str(self.traces[i].channel) + '</td>' \
          -328                        '<td >' + self.traces[i].sensorchoice.value + '</td>' \
          -329                            '<td>' + str(self.traces[i].gains.value) + '</td>')
          -330                traceinfo.appendChild(tr)
          -331        run_info.appendChild(traceinfo)
          -332        return run_info.asHTML()
          -333    
          -334    def _load_from_html(self, file):
          -335        """
          -336        Loads data and parameters for a completed run from a saved html file.
          -337        :param file: filename or path.
          -338        :return:
          -339        """
          -340        import pandas as pd
          -341        from JPSLUtils import find_pandas_dataframe_names
          -342        from AdvancedHTMLParser import AdvancedHTMLParser as parser
          -343        from plotly import graph_objects as go
          -344        whichrun = pd.read_html(file, attrs={'id': 'run_id'})[0]
          -345        run_title = whichrun['Title'][0]
          -346        run_id = whichrun['Id #'][0]
          -347        svname = pd.read_html(file, attrs={'id': 'file_info'})[0]['Saved ' \
          -348                                                                  'as'][0]
          -349        self.pandadf = pd.read_html(file, attrs={'class': 'dataframe'},
          -350                                    index_col=0)[0]
          -351        self.title = run_title
          -352        self.svname = svname
          -353        run_param = \
          -354        pd.read_html(file, attrs={'id': 'run_param'}, skiprows=[2])[0]
          -355        self.rate = run_param['Approx. Rate (Hz)'][0]
          -356        self.delta = run_param['Approx. Delta (s)'][0]
          -357        # reassiging timelbl to a value from a widget
          -358        self.timelbl = run_param['X-label'][0]
          -359        self.separate_plots = not (run_param['One Plot'][0])
          -360        xcols = list(map(int,run_param['X-cols'][0].replace('[',
          -361                                        '').replace(']','').split(',')))
          -362        ycols = list(map(int,run_param['Y-cols'][0].replace('[',
          -363                                        '').replace(']','').split(',')))
          -364        errcols = list(map(int,run_param['err-colsa'][0].replace('[',
          -365                                        '').replace(']', '').split(',')))
          -366        htmldatafile = parser(file)
          -367        self.defaultparamtxt = htmldatafile.getElementsByClassName(
          -368            'run_info')[0].asHTML()
          -369        traceinfo = pd.read_html(file, attrs={'id': 'traceinfo'})[0]
          -370        for k in traceinfo.index:
          -371            # Do not refill the widgets. This truncates and changes the
          -372            # definitions of some things from widgets to values.
          -373            self.traces[k].isactive = True
          -374            self.traces[k].tracelbl= traceinfo['Title'][k]
          -375            self.traces[k].units = traceinfo['Units'][k]
          -376            boardchoice, boardname = (traceinfo['Board'][k]).split(' ',1)
          -377            self.traces[k].boardchoice = boardchoice
          -378            self.traces[k].board = boardname
          -379            self.traces[k].channel = traceinfo['Channel'][k]
          -380            self.traces[k].gains= traceinfo['Gain'][k]
          -381            self.traces[k].sensor = traceinfo['Sensor'][k]
          -382        # Plot
          -383        if self.separate_plots:
          -384            self.livefig.set_subplots(rows=len(ycols), cols=1,
          -385                                                  shared_xaxes=True)
          -386            self.livefig.update_xaxes(
          -387                title=self.timelbl, row=len(ycols), col=1)
          -388        else:
          -389            self.livefig.update_xaxes(title=self.timelbl)
          -390            self.livefig.update_yaxes(title="Values")
          -391        for i in range(len(ycols)):
          -392            namestr = self.pandadf.columns[ycols[i]]
          -393            xcol = None
          -394            if len(xcols) == 1:
          -395                xcol = xcols[0]
          -396            else:
          -397                xcol = xcols[i]
          -398            scat = go.Scatter(
          -399                y=self.pandadf.iloc[0:, ycols[i]],
          -400                x=self.pandadf.iloc[0:,xcol], name=namestr)
          -401            if self.separate_plots:
          -402                self.livefig.update_yaxes(title=self.traces[i].units,
          -403                    row=i+1, col=1)
          -404                self.livefig.add_trace(scat, row=i+1, col=1)
          -405            else:
          -406                self.livefig.add_trace(scat)
          -407        pass
          -408
          -409    def setupclick(self, btn):
          -410        # Could just use the values in widgets, but this forces intentional
          -411        # selection and locks them for the run.
          -412        from copy import copy
          -413        self.title = copy(self.runtitle.value)
          -414        self.rate = copy(self.rateinp.value)
          -415        self.delta = 1 / self.rate
          -416        self.separate_plots = copy(self.separate_traces_checkbox.value)
          -417        self.defaultparamtxt = self._make_defaultparamtxt()
          -418        self.runtitle.close()
          -419        del self.runtitle
          -420        self.setup_layout.close()
          -421        del self.setup_layout
          -422        self.output.clear_output()
          -423        doRun(runs[self.idno-1])
          -424        with self.output:
          -425            display(runs[self.idno - 1].livefig)
          -426        pass
          -427
          -428    def setup(self):
          +205        self.runtitle = widgets.Text(
          +206            value=self.title,
          +207            placeholder='Type title/description',
          +208            description='Run title',
          +209            disabled=False)
          +210        self.defaultparamtxt = ''
          +211        self.defaultcollecttxt = '<span style="color:blue;"> To accurately '
          +212        self.defaultcollecttxt += 'read point location on graph you can zoom '
          +213        self.defaultcollecttxt += 'in. Drag to zoom. Additional controls show '
          +214        self.defaultcollecttxt += 'above plot when you hover over it.</span>'
          +215        self.collecttxt = widgets.HTML(
          +216            value=self.defaultcollecttxt,
          +217            placeholder='',
          +218            description='')
          +219        self.setup_layout_bottom = widgets.HBox(
          +220            [self.rateinp, self.timelbl, self.setupbtn])
          +221        self.setup_layout = widgets.VBox([self.separate_traces_checkbox,
          +222                                          self.setup_layout_bottom])
          +223        self.collect_layout = widgets.HBox([self.collectbtn, self.collecttxt])
          +224        self.output = widgets.Output()
          +225    def _make_defaultparamtxt(self):
          +226        """
          +227        Uses AdvancedHTMLParser (mimics javascript) to generate valid HTML for
          +228        the default parameter text.
          +229        :return: valid html string for the default parameter text.
          +230        """
          +231        from AdvancedHTMLParser import AdvancedTag as domel
          +232        run_info=domel('div')
          +233        run_info.setAttribute('id','DAQRun_' + str(self.idno) + '_info')
          +234        run_info.setAttribute('class','run_info')
          +235        run_id = domel('table')
          +236        run_id.setAttribute('id','run_id')
          +237        run_id.setAttribute('border', '1')
          +238        tr = domel('tr')
          +239        tr.appendInnerHTML('<th>Title</th><th>Id #</th>')
          +240        run_id.appendChild(tr)
          +241        tr = domel('tr')
          +242        tr.appendInnerHTML('<td>' + str(self.title) + '</td>' \
          +243                            '<td>' + str(self.idno) + '</td>')
          +244        run_id.appendChild(tr)
          +245        run_info.appendChild(run_id)
          +246        # table of run parameters
          +247        run_param = domel('table')
          +248        run_param.setAttribute('border','1')
          +249        run_param.setAttribute('id','run_param')
          +250        tr = domel('tr')
          +251        tr.setAttribute('style','text-align:center;')
          +252        tr.appendInnerHTML('<th>Approx. Rate (Hz)</th>' \
          +253                                '<th>Approx. Delta (s)</th>' \
          +254                                '<th>X-label </th>' \
          +255                                '<th>X-cols</th>' \
          +256                                '<th>Y-cols</th>' \
          +257                                '<th>err-cols<sup ' \
          +258                                'style="color:blue;">a</sup></th>' \
          +259                                '<th>One Plot</th>' )
          +260        run_param.appendChild(tr)
          +261        run_info.appendChild(run_param)
          +262        tr = domel('tr')
          +263        tr.setAttribute('style','text-align:center;')
          +264        tr.appendInnerHTML('<td>' + str(self.rate) + '</td>' \
          +265                            '<td>' + str(self.delta) + '</td>' \
          +266                            '<td>' + self.timelbl.value + '</td>')
          +267        xlist = '['
          +268        ylist = '['
          +269        errlist = '['
          +270        if self.ignore_skew:
          +271            xlist += '0]'
          +272            tempcount = 0
          +273            for k in self.traces:
          +274                if k.isactive:
          +275                    ylist += str(2 * tempcount + 1) + ','
          +276                    errlist += str(2 * tempcount + 2) + ','
          +277                    tempcount += 1
          +278            ylist = ylist[:-1] + ']'
          +279            errlist = errlist[:-1] + ']'
          +280        else:
          +281            tempcount = 0
          +282            for k in self.traces:
          +283                if k.isactive:
          +284                    xlist += str(3 * tempcount) + ','
          +285                    ylist += str(3 * tempcount + 1) + ','
          +286                    errlist += str(3 * tempcount + 2) + ','
          +287                    tempcount += 1
          +288            xlist = xlist[:-1] + ']'
          +289            ylist = ylist[:-1] + ']'
          +290            errlist = errlist[:-1] + ']'
          +291        tr.appendInnerHTML('<td>' + xlist + '</td><td>' + ylist + '</td>')
          +292        td = domel('td')
          +293        td.appendText(errlist)
          +294        tr.appendChild(td)
          +295        td = domel('td')
          +296        td.appendText(str(not (self.separate_plots)))
          +297        tr.appendChild(td)
          +298        run_param.appendChild(tr)
          +299        footer = domel('tfoot')
          +300        tr = domel('tr')
          +301        td = domel('td')
          +302        td.setAttribute('colspan','7')
          +303        td.appendInnerHTML('<sup style="color:blue;">a</sup>The ' \
          +304                            'standard deviation of the number in the ' \
          +305                            'column immediately to the left based on the ' \
          +306                            'variation in signal during the averaging time ' \
          +307                            'for the data point.')
          +308        tr.appendChild(td)
          +309        footer.appendChild(tr)
          +310        run_param.appendChild(footer)
          +311        # table of trace information
          +312        traceinfo = domel('table')
          +313        traceinfo.setAttribute('class','traceinfo')
          +314        traceinfo.setAttribute('id','traceinfo')
          +315        traceinfo.setAttribute('border','1')
          +316        tr = domel('tr')
          +317        tr.setAttribute('style','text-align:center;')
          +318        tr.appendInnerHTML('<th>Trace #</th><th>Title</th><th>Units</th>' \
          +319                            '<th>Board</th><th>Channel</th><th>Sensor</th>' \
          +320                            '<th>Gain</th>')
          +321        traceinfo.appendChild(tr)
          +322        for i in range(self.ntraces):
          +323            if (self.traces[i].isactive):
          +324                self.tracemap.append(i)
          +325                tr = domel('tr')
          +326                tr.setAttribute('style', 'text-align:center;')
          +327                tr.appendInnerHTML('<td>' + str(i) + '</td>' \
          +328                            '<td>' + self.traces[i].tracelbl.value + '</td>' \
          +329                            '<td>' + self.traces[i].units.value + '</td>' \
          +330                            '<td>' + str(self.traces[i].boardchoice.value) + \
          +331                            ' ' + self.traces[i].board.name + '</td>' \
          +332                            '<td>' + str(self.traces[i].channel) + '</td>' \
          +333                        '<td >' + self.traces[i].sensorchoice.value + '</td>' \
          +334                            '<td>' + str(self.traces[i].gains.value) + '</td>')
          +335                traceinfo.appendChild(tr)
          +336        run_info.appendChild(traceinfo)
          +337        return run_info.asHTML()
          +338    
          +339    def _load_from_html(self, file):
          +340        """
          +341        Loads data and parameters for a completed run from a saved html file.
          +342        :param file: filename or path.
          +343        :return:
          +344        """
          +345        import pandas as pd
          +346        from JPSLUtils import find_pandas_dataframe_names
          +347        from AdvancedHTMLParser import AdvancedHTMLParser as parser
          +348        from plotly import graph_objects as go
          +349        whichrun = pd.read_html(file, attrs={'id': 'run_id'})[0]
          +350        run_title = whichrun['Title'][0]
          +351        run_id = whichrun['Id #'][0]
          +352        svname = pd.read_html(file, attrs={'id': 'file_info'})[0]['Saved ' \
          +353                                                                  'as'][0]
          +354        self.pandadf = pd.read_html(file, attrs={'class': 'dataframe'},
          +355                                    index_col=0)[0]
          +356        self.title = run_title
          +357        self.svname = svname
          +358        run_param = \
          +359        pd.read_html(file, attrs={'id': 'run_param'}, skiprows=[2])[0]
          +360        self.rate = run_param['Approx. Rate (Hz)'][0]
          +361        self.delta = run_param['Approx. Delta (s)'][0]
          +362        # reassiging timelbl to a value from a widget
          +363        self.timelbl = run_param['X-label'][0]
          +364        self.separate_plots = not (run_param['One Plot'][0])
          +365        xcols = list(map(int,run_param['X-cols'][0].replace('[',
          +366                                        '').replace(']','').split(',')))
          +367        ycols = list(map(int,run_param['Y-cols'][0].replace('[',
          +368                                        '').replace(']','').split(',')))
          +369        errcols = list(map(int,run_param['err-colsa'][0].replace('[',
          +370                                        '').replace(']', '').split(',')))
          +371        htmldatafile = parser(file)
          +372        self.defaultparamtxt = htmldatafile.getElementsByClassName(
          +373            'run_info')[0].asHTML()
          +374        traceinfo = pd.read_html(file, attrs={'id': 'traceinfo'})[0]
          +375        for k in traceinfo.index:
          +376            # Do not refill the widgets. This truncates and changes the
          +377            # definitions of some things from widgets to values.
          +378            self.traces[k].isactive = True
          +379            self.traces[k].tracelbl= traceinfo['Title'][k]
          +380            self.traces[k].units = traceinfo['Units'][k]
          +381            boardchoice, boardname = (traceinfo['Board'][k]).split(' ',1)
          +382            self.traces[k].boardchoice = boardchoice
          +383            self.traces[k].board = boardname
          +384            self.traces[k].channel = traceinfo['Channel'][k]
          +385            self.traces[k].gains= traceinfo['Gain'][k]
          +386            self.traces[k].sensor = traceinfo['Sensor'][k]
          +387        # Plot
          +388        if self.separate_plots:
          +389            self.livefig.set_subplots(rows=len(ycols), cols=1,
          +390                                                  shared_xaxes=True)
          +391            self.livefig.update_xaxes(
          +392                title=self.timelbl, row=len(ycols), col=1)
          +393        else:
          +394            self.livefig.update_xaxes(title=self.timelbl)
          +395            self.livefig.update_yaxes(title="Values")
          +396        for i in range(len(ycols)):
          +397            namestr = self.pandadf.columns[ycols[i]]
          +398            xcol = None
          +399            if len(xcols) == 1:
          +400                xcol = xcols[0]
          +401            else:
          +402                xcol = xcols[i]
          +403            scat = go.Scatter(
          +404                y=self.pandadf.iloc[0:, ycols[i]],
          +405                x=self.pandadf.iloc[0:,xcol], name=namestr)
          +406            if self.separate_plots:
          +407                self.livefig.update_yaxes(title=self.traces[i].units,
          +408                    row=i+1, col=1)
          +409                self.livefig.add_trace(scat, row=i+1, col=1)
          +410            else:
          +411                self.livefig.add_trace(scat)
          +412        pass
          +413
          +414    def setupclick(self, btn):
          +415        # Could just use the values in widgets, but this forces intentional
          +416        # selection and locks them for the run.
          +417        from copy import copy
          +418        self.title = copy(self.runtitle.value)
          +419        self.rate = copy(self.rateinp.value)
          +420        self.delta = 1 / self.rate
          +421        self.separate_plots = copy(self.separate_traces_checkbox.value)
          +422        self.defaultparamtxt = self._make_defaultparamtxt()
          +423        self.runtitle.close()
          +424        del self.runtitle
          +425        self.setup_layout.close()
          +426        del self.setup_layout
          +427        self.output.clear_output()
          +428        doRun(runs[self.idno-1])
           429        with self.output:
          -430            display(HTML("<h3 id ='RunSetUp' "
          -431                         "style='text-align:center;'>Set Run Parameters</h3>"))
          -432            self.setupbtn.on_click(self.setupclick)
          -433            display(self.runtitle)
          -434            for i in range(self.ntraces):
          -435                self.traces[i].setup()
          -436            display(self.setup_layout)
          -437        display(self.output)
          -438        pass
          -439
          -440    def collectclick(self, btn):
          -441        if (btn.description == 'Start Collecting'):
          -442            btn.description = 'Stop Collecting'
          -443            btn.button_style = 'danger'
          -444            btn.tooltip = 'Stop the data collection'
          -445            # do not allow parameters to be reset after starting run.
          -446            self.setupbtn.disabled = True
          -447            self.setupbtn.tooltip = 'Parameters locked. The run has started.'
          -448            self.rateinp.disabled = True
          -449            self.timelbl.disabled = True
          -450            PLTconn, DAQconn = Pipe()
          -451            DAQCTL, PLTCTL = Pipe()
          -452            nactive = 0
          -453            for k in self.traces:
          -454                if k.isactive:
          -455                    nactive += 1
          -456            whichchn = []
          -457            gains =[]
          -458            for i in range(self.ntraces):
          -459                if (self.traces[i].isactive):
          -460                    brd = self.traces[i].board
          -461                    chn = self.traces[i].channel
          -462                    newchn = True
          -463                    if len(whichchn) > 0:
          -464                        for k in range(len(whichchn)):
          -465                            if whichchn[k]['board'] == brd \
          -466                                and whichchn[k]['chnl'] == chn:
          -467                                self.tracefrdatachn.append(k)
          -468                                newchn = False
          -469                    if newchn:
          -470                        whichchn.append({'board': brd,
          -471                                         'chnl': chn})
          -472                        gains.append(self.traces[i].toselectedgain)
          -473                        self.tracefrdatachn.append(len(whichchn)-1)
          -474            # Use up to 30% of the time for averaging if channels were spaced
          -475            # evenly between data collection times (with DACQ2 they appear
          -476            # more synchronous than that).
          -477            self.averaging_time = self.delta / nactive / 3
          -478            DAQ = Process(target=DAQProc,
          -479                          args=(
          -480                              whichchn, gains, self.averaging_time, self.delta,
          -481                              DAQconn, DAQCTL))
          -482            DAQ.start()
          -483            thread = threading.Thread(target=self.updatingplot, args=(
          -484                                        PLTconn, PLTCTL))
          -485            thread.start()
          -486            # self.updatingplot() hangs up user interface
          -487        else:
          -488            btn.description = 'Done'
          -489            btn.button_style = ''
          -490            btn.tooltip = ''
          -491            time.sleep(3)  # wait a few seconds for end of data collection
          -492            self.data = data
          -493            self.timestamp = timestamp
          -494            self.stdev = stdev
          -495            self.fillpandadf()
          -496            # save data to html file so it is human readable and can be loaded
          -497            # elsewhere.
          -498            #self.svname = self.title + '_' + time.strftime('%y-%m-%d_%H%M%S',
          -499                                       # time.localtime()) + '.html'
          -500            svhtml = '<!DOCTYPE html>' \
          -501                     '<html><body>'+ self.defaultparamtxt + \
          -502                     '<table id="file_info" border="1"><tr><th>Saved as ' \
          -503                     '</th></tr><tr><td>' +  \
          -504                     self.svname+'</td></tr></table>' \
          -505                     '<h2>DATA</h2>'+ \
          -506                     self.pandadf.to_html() + '</body></html>'
          -507            f = open(self.svname,'w')
          -508            f.write(svhtml)
          -509            f.close()
          -510            self.collectbtn.close()
          -511            del self.collectbtn
          -512            with self.output:
          -513                display(HTML(
          -514                    '<span style="color:blue;font-weight:bold;">DATA SAVED TO:' +
          -515                    self.svname + '</span>'))
          -516        return
          -517
          -518    def fillpandadf(self):
          -519        datacolumns = []
          -520        temptimes = np.transpose(self.timestamp)
          -521        tempdata = np.transpose(self.data)
          -522        tempstdev = np.transpose(self.stdev)
          -523        chncnt = 0
          -524        for i in range(self.ntraces):
          -525            if (self.traces[i].isactive):
          -526                chncnt += 1
          -527        for i in range(chncnt):
          -528            if self.ignore_skew and i > 0:
          -529                pass
          -530            else:
          -531                datacolumns.append(temptimes[i])
          -532            datacolumns.append(tempdata[i])
          -533            datacolumns.append(tempstdev[i])
          -534        titles = []
          -535        # Column labels.
          -536        chncnt = 0
          -537        for i in range(self.ntraces):
          -538            if (self.traces[i].isactive):
          -539                chncnt += 1
          -540                if self.ignore_skew:
          -541                    if chncnt == 1:
          -542                        titles.append(self.timelbl.value)
          -543                    pass
          -544                else:
          -545                    titles.append(self.traces[
          -546                                  i].tracelbl.value + '_' + self.timelbl.value)
          -547                titles.append(
          -548                    self.traces[i].tracelbl.value + '(' + self.traces[
          -549                        i].units.value + ')')
          -550                titles.append(
          -551                    self.traces[i].tracelbl.value + '_' + 'stdev')
          -552        #print(str(titles))
          -553        #print(str(datacolumns))
          -554        self.pandadf = pd.DataFrame(np.transpose(datacolumns), columns=titles)
          -555
          -556    def updatingplot(self, PLTconn, PLTCTL):
          -557        """
          -558        Runs until a check of self.collectbtn.description does not return
          -559        'Stop Collecting'. This would probably be more efficient if set a
          -560        boolean.
          -561        Parameters
          -562        ----------
          -563        PLTconn: Pipe
          -564            connection plotter end
          -565        DAQconn: Pipe
          -566            connection DAQ end
          -567        PLTCTL: Pipe
          -568            control pipe plotter end
          -569        DAQCTL: Pipe
          -570            control pipe DAQ end
          -571        """
          -572        starttime = time.time()
          -573        global data
          -574        data = []
          -575        global timestamp
          -576        timestamp = []
          -577        global stdev
          -578        stdev = []
          -579        datalegend = []
          -580        timelegend = []
          -581        stdevlegend = []
          -582        whichchn = []
          -583        gains = []
          -584        toplotx = []
          -585        toploty = []
          -586        nactive = 0
          -587        def convert_pkg():
          -588            plttime = 0
          -589            if self.ignore_skew:
          -590                plttime = sum(pkg[0]) / len(pkg[0])
          -591            traceidx = 0
          -592            tmptime = []
          -593            tmpavg = []
          -594            tmpstd = []
          -595            tmpavg_std = []
          -596            for i, k in zip(self.tracemap, self.tracefrdatachn):
          -597
          -598                avg = pkg[1][k]
          -599                std = pkg[2][k]
          -600                avg_std = pkg[3][k]
          -601                avg_vdd = pkg[4][k]
          -602                avg, std, avg_std = self.traces[i].toselectedunits(avg,
          -603                                                                   std, avg_std,
          -604                                                                   avg_vdd)
          -605                avg, std, avg_std = sensors. \
          -606                    to_reasonable_significant_figures_fast(avg, std, avg_std)
          -607                tmptime.append(pkg[0][k])
          -608                tmpavg.append(avg)
          -609                tmpstd.append(std)
          -610                tmpavg_std.append(avg_std)
          -611                if self.ignore_skew:
          -612                    toplotx[traceidx].append(plttime)
          -613                else:
          -614                    toplotx[traceidx].append(pkg[0][k])
          -615                toploty[traceidx].append(avg)
          -616                traceidx += 1
          -617            timestamp.append(tmptime)
          -618            data.append(tmpavg)
          -619            stdev.append(tmpavg_std)
          -620            return
          -621
          -622        for k in self.traces:
          -623            if k.isactive:
          -624                nactive += 1
          -625        if self.separate_plots:
          -626            self.livefig.set_subplots(rows = nactive, cols = 1,
          -627                                      shared_xaxes= True)
          -628            self.livefig.update_xaxes(title = self.timelbl.value,
          -629                                      row = nactive, col = 1)
          -630        else:
          -631            self.livefig.update_yaxes(title = "Values")
          -632            self.livefig.update_xaxes(title = self.timelbl.value)
          -633        active_count = 0
          -634        for i in range(self.ntraces):
          -635            if (self.traces[i].isactive):
          -636                active_count += 1
          -637                tempstr = self.traces[i].tracelbl.value + '(' + \
          -638                          self.traces[i].units.value + ')'
          -639                timelegend.append('time_' + tempstr)
          -640                datalegend.append(tempstr)
          -641                stdevlegend.append('stdev_' + tempstr)
          -642                if self.separate_plots:
          -643                    scat = go.Scatter(y=[],x=[], name=tempstr)
          -644                    self.livefig.add_trace(scat, row = active_count,
          -645                                           col = 1)
          -646                    self.livefig.update_yaxes(title = self.traces[
          -647                        i].units.value, row = active_count, col = 1)
          -648                else:
          -649                    self.livefig.add_scatter(y=[],x=[], name=tempstr)
          -650                toplotx.append([])
          -651                toploty.append([])
          -652        lastupdatetime = time.time()
          -653
          -654        pts = 0
          -655        oldpts = 0
          -656        #print('about to enter while loop',end='')
          -657        while (self.collectbtn.description == 'Stop Collecting'):
          -658            #print('.',end='')
          -659            while PLTconn.poll():
          -660                pkg = PLTconn.recv()
          -661                self.lastpkgstr = str(pkg)
          -662                #print(self.lastpkgstr)
          -663                # convert voltage to requested units.
          -664                convert_pkg()
          -665            currenttime = time.time()
          -666            mindelay = 1.0
          -667            if self.separate_traces_checkbox.value:
          -668                mindelay = nactive*1.0
          -669            else:
          -670                mindelay = nactive*0.5
          -671            if (currenttime - lastupdatetime)>(mindelay+len(toplotx[0])*len(
          -672                    toplotx)/1000):
          -673                lastupdatetime = currenttime
          -674                for k in range(len(self.livefig.data)):
          -675                    self.livefig.data[k].x=toplotx[k]
          -676                    self.livefig.data[k].y=toploty[k]
          -677            #time.sleep(1)
          -678            PLTCTL.send('send')
          -679            time.sleep(self.delta)
          -680            # print ('btn.description='+str(btn.description))
          -681        endtime = time.time()
          -682        PLTCTL.send('stop')
          -683        time.sleep(0.5)  # wait 0.5 second to collect remaining data
          -684        PLTCTL.send('send')
          -685        time.sleep(0.5)
          -686        msg = ''
          -687        while (msg != 'done'):
          -688            while PLTconn.poll():
          -689                pkg = PLTconn.recv()
          -690                # print(str(pkg))
          -691                # convert voltage to requested units.
          -692                convert_pkg()
          -693            PLTCTL.send('send')
          -694            time.sleep(0.2)
          -695            if PLTCTL.poll():
          -696                msg = PLTCTL.recv()
          -697                # print (str(msg))
          -698                if (msg != 'done'):
          -699                    print('Received unexpected message: ' + str(msg))
          -700        for k in range(len(self.livefig.data)):
          -701            self.livefig.data[k].x = toplotx[k]
          -702            self.livefig.data[k].y = toploty[k]
          +430            display(runs[self.idno - 1].livefig)
          +431        pass
          +432
          +433    def setup(self):
          +434        with self.output:
          +435            display(HTML("<h3 id ='RunSetUp' "
          +436                         "style='text-align:center;'>Set Run Parameters</h3>"))
          +437            self.setupbtn.on_click(self.setupclick)
          +438            display(self.runtitle)
          +439            for i in range(self.ntraces):
          +440                self.traces[i].setup()
          +441            display(self.setup_layout)
          +442        display(self.output)
          +443        pass
          +444
          +445    def collectclick(self, btn):
          +446        if (btn.description == 'Start Collecting'):
          +447            btn.description = 'Stop Collecting'
          +448            btn.button_style = 'danger'
          +449            btn.tooltip = 'Stop the data collection'
          +450            # do not allow parameters to be reset after starting run.
          +451            self.setupbtn.disabled = True
          +452            self.setupbtn.tooltip = 'Parameters locked. The run has started.'
          +453            self.rateinp.disabled = True
          +454            self.timelbl.disabled = True
          +455            nactive = 0
          +456            for k in self.traces:
          +457                if k.isactive:
          +458                    nactive += 1
          +459            whichchn = []
          +460            gains =[]
          +461            for i in range(self.ntraces):
          +462                if (self.traces[i].isactive):
          +463                    brd = self.traces[i].board
          +464                    chn = self.traces[i].channel
          +465                    newchn = True
          +466                    if len(whichchn) > 0:
          +467                        for k in range(len(whichchn)):
          +468                            if whichchn[k]['board'] == brd \
          +469                                and whichchn[k]['chnl'] == chn:
          +470                                self.tracefrdatachn.append(k)
          +471                                newchn = False
          +472                    if newchn:
          +473                        whichchn.append({'board': brd,
          +474                                         'chnl': chn})
          +475                        gains.append(self.traces[i].toselectedgain)
          +476                        self.tracefrdatachn.append(len(whichchn)-1)
          +477            # Use up to 30% of the time for averaging if channels were spaced
          +478            # evenly between data collection times (with DACQ2 they appear
          +479            # more synchronous than that).
          +480            self.averaging_time = self.delta / nactive / 3
          +481            DAQ = Process(target=DAQProc,
          +482                          args=(
          +483                              whichchn, gains, self.averaging_time, self.delta,
          +484                              self.DAQconn, self.DAQCTL))
          +485            DAQ.start()
          +486            self.pltthread.start()
          +487            # self.updatingplot() hangs up user interface
          +488        else:
          +489            btn.description = 'Done'
          +490            btn.button_style = ''
          +491            btn.tooltip = ''
          +492            # wait a plotting thread to terminate
          +493            self.pltthread.join()
          +494            self.data = data
          +495            self.timestamp = timestamp
          +496            self.stdev = stdev
          +497            self.fillpandadf()
          +498            # save data to html file so it is human readable and can be loaded
          +499            # elsewhere.
          +500            #self.svname = self.title + '_' + time.strftime('%y-%m-%d_%H%M%S',
          +501                                       # time.localtime()) + '.html'
          +502            svhtml = '<!DOCTYPE html>' \
          +503                     '<html><body>'+ self.defaultparamtxt + \
          +504                     '<table id="file_info" border="1"><tr><th>Saved as ' \
          +505                     '</th></tr><tr><td>' +  \
          +506                     self.svname+'</td></tr></table>' \
          +507                     '<h2>DATA</h2>'+ \
          +508                     self.pandadf.to_html() + '</body></html>'
          +509            f = open(self.svname,'w')
          +510            f.write(svhtml)
          +511            f.close()
          +512            self.collectbtn.close()
          +513            del self.collectbtn
          +514            with self.output:
          +515                display(HTML(
          +516                    '<span style="color:blue;font-weight:bold;">DATA SAVED TO:' +
          +517                    self.svname + '</span>'))
          +518        return
          +519
          +520    def fillpandadf(self):
          +521        datacolumns = []
          +522        temptimes = np.transpose(self.timestamp)
          +523        tempdata = np.transpose(self.data)
          +524        tempstdev = np.transpose(self.stdev)
          +525        chncnt = 0
          +526        for i in range(self.ntraces):
          +527            if (self.traces[i].isactive):
          +528                chncnt += 1
          +529        for i in range(chncnt):
          +530            if self.ignore_skew and i > 0:
          +531                pass
          +532            else:
          +533                datacolumns.append(temptimes[i])
          +534            datacolumns.append(tempdata[i])
          +535            datacolumns.append(tempstdev[i])
          +536        titles = []
          +537        # Column labels.
          +538        chncnt = 0
          +539        for i in range(self.ntraces):
          +540            if (self.traces[i].isactive):
          +541                chncnt += 1
          +542                if self.ignore_skew:
          +543                    if chncnt == 1:
          +544                        titles.append(self.timelbl.value)
          +545                    pass
          +546                else:
          +547                    titles.append(self.traces[
          +548                                  i].tracelbl.value + '_' + self.timelbl.value)
          +549                titles.append(
          +550                    self.traces[i].tracelbl.value + '(' + self.traces[
          +551                        i].units.value + ')')
          +552                titles.append(
          +553                    self.traces[i].tracelbl.value + '_' + 'stdev')
          +554        #print(str(titles))
          +555        #print(str(datacolumns))
          +556        self.pandadf = pd.DataFrame(np.transpose(datacolumns), columns=titles)
          +557
          +558    def updatingplot(self, PLTconn, PLTCTL):
          +559        """
          +560        Runs until a check of self.collectbtn.description does not return
          +561        'Stop Collecting'. This would probably be more efficient if set a
          +562        boolean.
          +563        Parameters
          +564        ----------
          +565        PLTconn: Pipe
          +566            connection plotter end
          +567        DAQconn: Pipe
          +568            connection DAQ end
          +569        PLTCTL: Pipe
          +570            control pipe plotter end
          +571        DAQCTL: Pipe
          +572            control pipe DAQ end
          +573        """
          +574        starttime = time.time()
          +575        global data
          +576        data = []
          +577        global timestamp
          +578        timestamp = []
          +579        global stdev
          +580        stdev = []
          +581        datalegend = []
          +582        timelegend = []
          +583        stdevlegend = []
          +584        whichchn = []
          +585        gains = []
          +586        toplotx = []
          +587        toploty = []
          +588        nactive = 0
          +589        def convert_pkg():
          +590            plttime = 0
          +591            if self.ignore_skew:
          +592                plttime = sum(pkg[0]) / len(pkg[0])
          +593            traceidx = 0
          +594            tmptime = []
          +595            tmpavg = []
          +596            tmpstd = []
          +597            tmpavg_std = []
          +598            for i, k in zip(self.tracemap, self.tracefrdatachn):
          +599
          +600                avg = pkg[1][k]
          +601                std = pkg[2][k]
          +602                avg_std = pkg[3][k]
          +603                avg_vdd = pkg[4][k]
          +604                avg, std, avg_std = self.traces[i].toselectedunits(avg,
          +605                                                                   std, avg_std,
          +606                                                                   avg_vdd)
          +607                avg, std, avg_std = sensors. \
          +608                    to_reasonable_significant_figures_fast(avg, std, avg_std)
          +609                tmptime.append(pkg[0][k])
          +610                tmpavg.append(avg)
          +611                tmpstd.append(std)
          +612                tmpavg_std.append(avg_std)
          +613                if self.ignore_skew:
          +614                    toplotx[traceidx].append(plttime)
          +615                else:
          +616                    toplotx[traceidx].append(pkg[0][k])
          +617                toploty[traceidx].append(avg)
          +618                traceidx += 1
          +619            timestamp.append(tmptime)
          +620            data.append(tmpavg)
          +621            stdev.append(tmpavg_std)
          +622            return
          +623
          +624        for k in self.traces:
          +625            if k.isactive:
          +626                nactive += 1
          +627        if self.separate_plots:
          +628            self.livefig.set_subplots(rows = nactive, cols = 1,
          +629                                      shared_xaxes= True)
          +630            self.livefig.update_xaxes(title = self.timelbl.value,
          +631                                      row = nactive, col = 1)
          +632        else:
          +633            self.livefig.update_yaxes(title = "Values")
          +634            self.livefig.update_xaxes(title = self.timelbl.value)
          +635        active_count = 0
          +636        for i in range(self.ntraces):
          +637            if (self.traces[i].isactive):
          +638                active_count += 1
          +639                tempstr = self.traces[i].tracelbl.value + '(' + \
          +640                          self.traces[i].units.value + ')'
          +641                timelegend.append('time_' + tempstr)
          +642                datalegend.append(tempstr)
          +643                stdevlegend.append('stdev_' + tempstr)
          +644                if self.separate_plots:
          +645                    scat = go.Scatter(y=[],x=[], name=tempstr)
          +646                    self.livefig.add_trace(scat, row = active_count,
          +647                                           col = 1)
          +648                    self.livefig.update_yaxes(title = self.traces[
          +649                        i].units.value, row = active_count, col = 1)
          +650                else:
          +651                    self.livefig.add_scatter(y=[],x=[], name=tempstr)
          +652                toplotx.append([])
          +653                toploty.append([])
          +654        lastupdatetime = time.time()
          +655
          +656        pts = 0
          +657        oldpts = 0
          +658        #print('about to enter while loop',end='')
          +659        while (self.collectbtn.description == 'Stop Collecting'):
          +660            #print('.',end='')
          +661            while PLTconn.poll():
          +662                pkg = PLTconn.recv()
          +663                self.lastpkgstr = str(pkg)
          +664                #print(self.lastpkgstr)
          +665                # convert voltage to requested units.
          +666                convert_pkg()
          +667            currenttime = time.time()
          +668            mindelay = 1.0
          +669            if self.separate_traces_checkbox.value:
          +670                mindelay = nactive*1.0
          +671            else:
          +672                mindelay = nactive*0.5
          +673            if (currenttime - lastupdatetime)>(mindelay+len(toplotx[0])*len(
          +674                    toplotx)/1000):
          +675                lastupdatetime = currenttime
          +676                for k in range(len(self.livefig.data)):
          +677                    self.livefig.data[k].x=toplotx[k]
          +678                    self.livefig.data[k].y=toploty[k]
          +679            #time.sleep(1)
          +680            PLTCTL.send('send')
          +681            time.sleep(self.delta)
          +682            # print ('btn.description='+str(btn.description))
          +683        endtime = time.time()
          +684        PLTCTL.send('stop')
          +685        time.sleep(0.5)  # wait 0.5 second to collect remaining data
          +686        PLTCTL.send('send')
          +687        time.sleep(0.5)
          +688        msg = ''
          +689        while (msg != 'done'):
          +690            while PLTconn.poll():
          +691                pkg = PLTconn.recv()
          +692                # print(str(pkg))
          +693                # convert voltage to requested units.
          +694                convert_pkg()
          +695            PLTCTL.send('send')
          +696            time.sleep(0.2)
          +697            if PLTCTL.poll():
          +698                msg = PLTCTL.recv()
          +699                # print (str(msg))
          +700                if (msg != 'done'):
          +701                    print('Received unexpected message: ' + str(msg))
          +702        for k in range(len(self.livefig.data)):
          +703            self.livefig.data[k].x = toplotx[k]
          +704            self.livefig.data[k].y = toploty[k]
          +705        return
           
          @@ -1604,99 +1609,103 @@

          -
          127    def __init__(self, idno, title='None', ntraces=4, **kwargs):
          -128        """
          -129        Data Aquistion Instance (a run).
          -130
          -131        :param idno : id number you wish to use to keep track
          -132        :param title: optional name
          -133        :param ntraces: number of traces (default = 4) more than 4 easily
          -134            overwhelms a pi4.
          -135        :param kwargs:
          -136            :ignore_skew: bool (default: True) if True only a single average
          -137            collection time will be recorded for each time in a multichannel
          -138            data collection. If False a separate set of time will be
          -139            recorded for each channel.
          -140        """
          -141        from plotly import graph_objects as go
          -142        self.ignore_skew = kwargs.pop('ignore_skew',True)
          -143        self.idno = idno
          -144        self.livefig = go.FigureWidget(layout_template='simple_white')
          -145        self.title = str(title)
          -146        self.svname = title + '.jpidaq.html'
          -147        self.averaging_time = 0.1  # seconds adjusted based on collection rate
          -148        self.gain = [1] * ntraces
          -149        self.data = []
          -150        self.timestamp = []
          -151        self.stdev = []
          -152        self.pandadf = None
          -153        self.ntraces = ntraces
          -154        self.separate_plots = True
          -155        self.traces = []
          -156        # index map from returned data to trace,
          -157        self.tracemap = []
          -158        self.tracefrdatachn = []
          -159        self.tracelbls = []
          -160        self.units = []
          -161        for i in range(self.ntraces):
          -162            self.traces.append(ChannelSettings(i, availboards))
          -163        self.ratemax = 20.0  # Hz
          -164        self.rate = 1.0  # Hz
          -165        self.deltamin = 1 / self.ratemax
          -166        self.delta = 1.0 / self.rate
          -167        self.setupbtn = widgets.Button(
          -168            description='Set Parameters',
          -169            disabled=False,
          -170            button_style='info',
          -171            # 'success', 'info', 'warning', 'danger' or ''
          -172            tooltip='Click to set collection parameters to displayed values.',
          -173            icon='')
          -174        self.collectbtn = widgets.Button(
          -175            description='Start Collecting',
          -176            disabled=False,
          -177            button_style='success',
          -178            # 'success', 'info', 'warning', 'danger' or ''
          -179            tooltip='Start collecting data and plotting it. '
          -180                    'Will make new graph.',
          -181            icon='')
          -182        self.separate_traces_checkbox = widgets.Checkbox(
          -183            value = self.separate_plots,
          -184            description = 'Display multiple traces as stacked graphs. ' \
          -185                        'Uncheck to display all on the same graph.',
          -186            layout = widgets.Layout(width='80%'),
          -187            disabled = False)
          -188        self.rateinp = widgets.BoundedFloatText(
          -189            value=self.rate,
          -190            min=0,
          -191            max=self.ratemax,
          -192            step=self.ratemax / 1000.0,
          -193            description='Rate (Hz):',
          -194            disabled=False)
          -195        self.timelbl = widgets.Text(
          -196            value='Time(s)',
          -197            placeholder='Type something',
          -198            description='X-axis label (time):',
          +            
          128    def __init__(self, idno, title='None', ntraces=4, **kwargs):
          +129        """
          +130        Data Aquistion Instance (a run).
          +131
          +132        :param idno : id number you wish to use to keep track
          +133        :param title: optional name
          +134        :param ntraces: number of traces (default = 4) more than 4 easily
          +135            overwhelms a pi4.
          +136        :param kwargs:
          +137            :ignore_skew: bool (default: True) if True only a single average
          +138            collection time will be recorded for each time in a multichannel
          +139            data collection. If False a separate set of time will be
          +140            recorded for each channel.
          +141        """
          +142        from plotly import graph_objects as go
          +143        self.ignore_skew = kwargs.pop('ignore_skew',True)
          +144        self.idno = idno
          +145        self.livefig = go.FigureWidget(layout_template='simple_white')
          +146        self.PLTconn, self.DAQconn = Pipe()
          +147        self.DAQCTL, self.PLTCTL = Pipe()
          +148        self.pltthread = threading.Thread(target=self.updatingplot, args=(
          +149                                        self.PLTconn, self.PLTCTL))
          +150        self.title = str(title)
          +151        self.svname = title + '.jpidaq.html'
          +152        self.averaging_time = 0.1  # seconds adjusted based on collection rate
          +153        self.gain = [1] * ntraces
          +154        self.data = []
          +155        self.timestamp = []
          +156        self.stdev = []
          +157        self.pandadf = None
          +158        self.ntraces = ntraces
          +159        self.separate_plots = True
          +160        self.traces = []
          +161        # index map from returned data to trace,
          +162        self.tracemap = []
          +163        self.tracefrdatachn = []
          +164        self.tracelbls = []
          +165        self.units = []
          +166        for i in range(self.ntraces):
          +167            self.traces.append(ChannelSettings(i, availboards))
          +168        self.ratemax = 20.0  # Hz
          +169        self.rate = 1.0  # Hz
          +170        self.deltamin = 1 / self.ratemax
          +171        self.delta = 1.0 / self.rate
          +172        self.setupbtn = widgets.Button(
          +173            description='Set Parameters',
          +174            disabled=False,
          +175            button_style='info',
          +176            # 'success', 'info', 'warning', 'danger' or ''
          +177            tooltip='Click to set collection parameters to displayed values.',
          +178            icon='')
          +179        self.collectbtn = widgets.Button(
          +180            description='Start Collecting',
          +181            disabled=False,
          +182            button_style='success',
          +183            # 'success', 'info', 'warning', 'danger' or ''
          +184            tooltip='Start collecting data and plotting it. '
          +185                    'Will make new graph.',
          +186            icon='')
          +187        self.separate_traces_checkbox = widgets.Checkbox(
          +188            value = self.separate_plots,
          +189            description = 'Display multiple traces as stacked graphs. ' \
          +190                        'Uncheck to display all on the same graph.',
          +191            layout = widgets.Layout(width='80%'),
          +192            disabled = False)
          +193        self.rateinp = widgets.BoundedFloatText(
          +194            value=self.rate,
          +195            min=0,
          +196            max=self.ratemax,
          +197            step=self.ratemax / 1000.0,
          +198            description='Rate (Hz):',
           199            disabled=False)
          -200        self.runtitle = widgets.Text(
          -201            value=self.title,
          -202            placeholder='Type title/description',
          -203            description='Run title',
          +200        self.timelbl = widgets.Text(
          +201            value='Time(s)',
          +202            placeholder='Type something',
          +203            description='X-axis label (time):',
           204            disabled=False)
          -205        self.defaultparamtxt = ''
          -206        self.defaultcollecttxt = '<span style="color:blue;"> To accurately '
          -207        self.defaultcollecttxt += 'read point location on graph you can zoom '
          -208        self.defaultcollecttxt += 'in. Drag to zoom. Additional controls show '
          -209        self.defaultcollecttxt += 'above plot when you hover over it.</span>'
          -210        self.collecttxt = widgets.HTML(
          -211            value=self.defaultcollecttxt,
          -212            placeholder='',
          -213            description='')
          -214        self.setup_layout_bottom = widgets.HBox(
          -215            [self.rateinp, self.timelbl, self.setupbtn])
          -216        self.setup_layout = widgets.VBox([self.separate_traces_checkbox,
          -217                                          self.setup_layout_bottom])
          -218        self.collect_layout = widgets.HBox([self.collectbtn, self.collecttxt])
          -219        self.output = widgets.Output()
          +205        self.runtitle = widgets.Text(
          +206            value=self.title,
          +207            placeholder='Type title/description',
          +208            description='Run title',
          +209            disabled=False)
          +210        self.defaultparamtxt = ''
          +211        self.defaultcollecttxt = '<span style="color:blue;"> To accurately '
          +212        self.defaultcollecttxt += 'read point location on graph you can zoom '
          +213        self.defaultcollecttxt += 'in. Drag to zoom. Additional controls show '
          +214        self.defaultcollecttxt += 'above plot when you hover over it.</span>'
          +215        self.collecttxt = widgets.HTML(
          +216            value=self.defaultcollecttxt,
          +217            placeholder='',
          +218            description='')
          +219        self.setup_layout_bottom = widgets.HBox(
          +220            [self.rateinp, self.timelbl, self.setupbtn])
          +221        self.setup_layout = widgets.VBox([self.separate_traces_checkbox,
          +222                                          self.setup_layout_bottom])
          +223        self.collect_layout = widgets.HBox([self.collectbtn, self.collecttxt])
          +224        self.output = widgets.Output()
           
          @@ -1730,24 +1739,24 @@
          Parameters
          -
          409    def setupclick(self, btn):
          -410        # Could just use the values in widgets, but this forces intentional
          -411        # selection and locks them for the run.
          -412        from copy import copy
          -413        self.title = copy(self.runtitle.value)
          -414        self.rate = copy(self.rateinp.value)
          -415        self.delta = 1 / self.rate
          -416        self.separate_plots = copy(self.separate_traces_checkbox.value)
          -417        self.defaultparamtxt = self._make_defaultparamtxt()
          -418        self.runtitle.close()
          -419        del self.runtitle
          -420        self.setup_layout.close()
          -421        del self.setup_layout
          -422        self.output.clear_output()
          -423        doRun(runs[self.idno-1])
          -424        with self.output:
          -425            display(runs[self.idno - 1].livefig)
          -426        pass
          +            
          414    def setupclick(self, btn):
          +415        # Could just use the values in widgets, but this forces intentional
          +416        # selection and locks them for the run.
          +417        from copy import copy
          +418        self.title = copy(self.runtitle.value)
          +419        self.rate = copy(self.rateinp.value)
          +420        self.delta = 1 / self.rate
          +421        self.separate_plots = copy(self.separate_traces_checkbox.value)
          +422        self.defaultparamtxt = self._make_defaultparamtxt()
          +423        self.runtitle.close()
          +424        del self.runtitle
          +425        self.setup_layout.close()
          +426        del self.setup_layout
          +427        self.output.clear_output()
          +428        doRun(runs[self.idno-1])
          +429        with self.output:
          +430            display(runs[self.idno - 1].livefig)
          +431        pass
           
          @@ -1765,17 +1774,17 @@
          Parameters
          -
          428    def setup(self):
          -429        with self.output:
          -430            display(HTML("<h3 id ='RunSetUp' "
          -431                         "style='text-align:center;'>Set Run Parameters</h3>"))
          -432            self.setupbtn.on_click(self.setupclick)
          -433            display(self.runtitle)
          -434            for i in range(self.ntraces):
          -435                self.traces[i].setup()
          -436            display(self.setup_layout)
          -437        display(self.output)
          -438        pass
          +            
          433    def setup(self):
          +434        with self.output:
          +435            display(HTML("<h3 id ='RunSetUp' "
          +436                         "style='text-align:center;'>Set Run Parameters</h3>"))
          +437            self.setupbtn.on_click(self.setupclick)
          +438            display(self.runtitle)
          +439            for i in range(self.ntraces):
          +440                self.traces[i].setup()
          +441            display(self.setup_layout)
          +442        display(self.output)
          +443        pass
           
          @@ -1793,83 +1802,80 @@
          Parameters
          -
          440    def collectclick(self, btn):
          -441        if (btn.description == 'Start Collecting'):
          -442            btn.description = 'Stop Collecting'
          -443            btn.button_style = 'danger'
          -444            btn.tooltip = 'Stop the data collection'
          -445            # do not allow parameters to be reset after starting run.
          -446            self.setupbtn.disabled = True
          -447            self.setupbtn.tooltip = 'Parameters locked. The run has started.'
          -448            self.rateinp.disabled = True
          -449            self.timelbl.disabled = True
          -450            PLTconn, DAQconn = Pipe()
          -451            DAQCTL, PLTCTL = Pipe()
          -452            nactive = 0
          -453            for k in self.traces:
          -454                if k.isactive:
          -455                    nactive += 1
          -456            whichchn = []
          -457            gains =[]
          -458            for i in range(self.ntraces):
          -459                if (self.traces[i].isactive):
          -460                    brd = self.traces[i].board
          -461                    chn = self.traces[i].channel
          -462                    newchn = True
          -463                    if len(whichchn) > 0:
          -464                        for k in range(len(whichchn)):
          -465                            if whichchn[k]['board'] == brd \
          -466                                and whichchn[k]['chnl'] == chn:
          -467                                self.tracefrdatachn.append(k)
          -468                                newchn = False
          -469                    if newchn:
          -470                        whichchn.append({'board': brd,
          -471                                         'chnl': chn})
          -472                        gains.append(self.traces[i].toselectedgain)
          -473                        self.tracefrdatachn.append(len(whichchn)-1)
          -474            # Use up to 30% of the time for averaging if channels were spaced
          -475            # evenly between data collection times (with DACQ2 they appear
          -476            # more synchronous than that).
          -477            self.averaging_time = self.delta / nactive / 3
          -478            DAQ = Process(target=DAQProc,
          -479                          args=(
          -480                              whichchn, gains, self.averaging_time, self.delta,
          -481                              DAQconn, DAQCTL))
          -482            DAQ.start()
          -483            thread = threading.Thread(target=self.updatingplot, args=(
          -484                                        PLTconn, PLTCTL))
          -485            thread.start()
          -486            # self.updatingplot() hangs up user interface
          -487        else:
          -488            btn.description = 'Done'
          -489            btn.button_style = ''
          -490            btn.tooltip = ''
          -491            time.sleep(3)  # wait a few seconds for end of data collection
          -492            self.data = data
          -493            self.timestamp = timestamp
          -494            self.stdev = stdev
          -495            self.fillpandadf()
          -496            # save data to html file so it is human readable and can be loaded
          -497            # elsewhere.
          -498            #self.svname = self.title + '_' + time.strftime('%y-%m-%d_%H%M%S',
          -499                                       # time.localtime()) + '.html'
          -500            svhtml = '<!DOCTYPE html>' \
          -501                     '<html><body>'+ self.defaultparamtxt + \
          -502                     '<table id="file_info" border="1"><tr><th>Saved as ' \
          -503                     '</th></tr><tr><td>' +  \
          -504                     self.svname+'</td></tr></table>' \
          -505                     '<h2>DATA</h2>'+ \
          -506                     self.pandadf.to_html() + '</body></html>'
          -507            f = open(self.svname,'w')
          -508            f.write(svhtml)
          -509            f.close()
          -510            self.collectbtn.close()
          -511            del self.collectbtn
          -512            with self.output:
          -513                display(HTML(
          -514                    '<span style="color:blue;font-weight:bold;">DATA SAVED TO:' +
          -515                    self.svname + '</span>'))
          -516        return
          +            
          445    def collectclick(self, btn):
          +446        if (btn.description == 'Start Collecting'):
          +447            btn.description = 'Stop Collecting'
          +448            btn.button_style = 'danger'
          +449            btn.tooltip = 'Stop the data collection'
          +450            # do not allow parameters to be reset after starting run.
          +451            self.setupbtn.disabled = True
          +452            self.setupbtn.tooltip = 'Parameters locked. The run has started.'
          +453            self.rateinp.disabled = True
          +454            self.timelbl.disabled = True
          +455            nactive = 0
          +456            for k in self.traces:
          +457                if k.isactive:
          +458                    nactive += 1
          +459            whichchn = []
          +460            gains =[]
          +461            for i in range(self.ntraces):
          +462                if (self.traces[i].isactive):
          +463                    brd = self.traces[i].board
          +464                    chn = self.traces[i].channel
          +465                    newchn = True
          +466                    if len(whichchn) > 0:
          +467                        for k in range(len(whichchn)):
          +468                            if whichchn[k]['board'] == brd \
          +469                                and whichchn[k]['chnl'] == chn:
          +470                                self.tracefrdatachn.append(k)
          +471                                newchn = False
          +472                    if newchn:
          +473                        whichchn.append({'board': brd,
          +474                                         'chnl': chn})
          +475                        gains.append(self.traces[i].toselectedgain)
          +476                        self.tracefrdatachn.append(len(whichchn)-1)
          +477            # Use up to 30% of the time for averaging if channels were spaced
          +478            # evenly between data collection times (with DACQ2 they appear
          +479            # more synchronous than that).
          +480            self.averaging_time = self.delta / nactive / 3
          +481            DAQ = Process(target=DAQProc,
          +482                          args=(
          +483                              whichchn, gains, self.averaging_time, self.delta,
          +484                              self.DAQconn, self.DAQCTL))
          +485            DAQ.start()
          +486            self.pltthread.start()
          +487            # self.updatingplot() hangs up user interface
          +488        else:
          +489            btn.description = 'Done'
          +490            btn.button_style = ''
          +491            btn.tooltip = ''
          +492            # wait a plotting thread to terminate
          +493            self.pltthread.join()
          +494            self.data = data
          +495            self.timestamp = timestamp
          +496            self.stdev = stdev
          +497            self.fillpandadf()
          +498            # save data to html file so it is human readable and can be loaded
          +499            # elsewhere.
          +500            #self.svname = self.title + '_' + time.strftime('%y-%m-%d_%H%M%S',
          +501                                       # time.localtime()) + '.html'
          +502            svhtml = '<!DOCTYPE html>' \
          +503                     '<html><body>'+ self.defaultparamtxt + \
          +504                     '<table id="file_info" border="1"><tr><th>Saved as ' \
          +505                     '</th></tr><tr><td>' +  \
          +506                     self.svname+'</td></tr></table>' \
          +507                     '<h2>DATA</h2>'+ \
          +508                     self.pandadf.to_html() + '</body></html>'
          +509            f = open(self.svname,'w')
          +510            f.write(svhtml)
          +511            f.close()
          +512            self.collectbtn.close()
          +513            del self.collectbtn
          +514            with self.output:
          +515                display(HTML(
          +516                    '<span style="color:blue;font-weight:bold;">DATA SAVED TO:' +
          +517                    self.svname + '</span>'))
          +518        return
           
          @@ -1887,43 +1893,43 @@
          Parameters
          -
          518    def fillpandadf(self):
          -519        datacolumns = []
          -520        temptimes = np.transpose(self.timestamp)
          -521        tempdata = np.transpose(self.data)
          -522        tempstdev = np.transpose(self.stdev)
          -523        chncnt = 0
          -524        for i in range(self.ntraces):
          -525            if (self.traces[i].isactive):
          -526                chncnt += 1
          -527        for i in range(chncnt):
          -528            if self.ignore_skew and i > 0:
          -529                pass
          -530            else:
          -531                datacolumns.append(temptimes[i])
          -532            datacolumns.append(tempdata[i])
          -533            datacolumns.append(tempstdev[i])
          -534        titles = []
          -535        # Column labels.
          -536        chncnt = 0
          -537        for i in range(self.ntraces):
          -538            if (self.traces[i].isactive):
          -539                chncnt += 1
          -540                if self.ignore_skew:
          -541                    if chncnt == 1:
          -542                        titles.append(self.timelbl.value)
          -543                    pass
          -544                else:
          -545                    titles.append(self.traces[
          -546                                  i].tracelbl.value + '_' + self.timelbl.value)
          -547                titles.append(
          -548                    self.traces[i].tracelbl.value + '(' + self.traces[
          -549                        i].units.value + ')')
          -550                titles.append(
          -551                    self.traces[i].tracelbl.value + '_' + 'stdev')
          -552        #print(str(titles))
          -553        #print(str(datacolumns))
          -554        self.pandadf = pd.DataFrame(np.transpose(datacolumns), columns=titles)
          +            
          520    def fillpandadf(self):
          +521        datacolumns = []
          +522        temptimes = np.transpose(self.timestamp)
          +523        tempdata = np.transpose(self.data)
          +524        tempstdev = np.transpose(self.stdev)
          +525        chncnt = 0
          +526        for i in range(self.ntraces):
          +527            if (self.traces[i].isactive):
          +528                chncnt += 1
          +529        for i in range(chncnt):
          +530            if self.ignore_skew and i > 0:
          +531                pass
          +532            else:
          +533                datacolumns.append(temptimes[i])
          +534            datacolumns.append(tempdata[i])
          +535            datacolumns.append(tempstdev[i])
          +536        titles = []
          +537        # Column labels.
          +538        chncnt = 0
          +539        for i in range(self.ntraces):
          +540            if (self.traces[i].isactive):
          +541                chncnt += 1
          +542                if self.ignore_skew:
          +543                    if chncnt == 1:
          +544                        titles.append(self.timelbl.value)
          +545                    pass
          +546                else:
          +547                    titles.append(self.traces[
          +548                                  i].tracelbl.value + '_' + self.timelbl.value)
          +549                titles.append(
          +550                    self.traces[i].tracelbl.value + '(' + self.traces[
          +551                        i].units.value + ')')
          +552                titles.append(
          +553                    self.traces[i].tracelbl.value + '_' + 'stdev')
          +554        #print(str(titles))
          +555        #print(str(datacolumns))
          +556        self.pandadf = pd.DataFrame(np.transpose(datacolumns), columns=titles)
           
          @@ -1941,153 +1947,154 @@
          Parameters
          -
          556    def updatingplot(self, PLTconn, PLTCTL):
          -557        """
          -558        Runs until a check of self.collectbtn.description does not return
          -559        'Stop Collecting'. This would probably be more efficient if set a
          -560        boolean.
          -561        Parameters
          -562        ----------
          -563        PLTconn: Pipe
          -564            connection plotter end
          -565        DAQconn: Pipe
          -566            connection DAQ end
          -567        PLTCTL: Pipe
          -568            control pipe plotter end
          -569        DAQCTL: Pipe
          -570            control pipe DAQ end
          -571        """
          -572        starttime = time.time()
          -573        global data
          -574        data = []
          -575        global timestamp
          -576        timestamp = []
          -577        global stdev
          -578        stdev = []
          -579        datalegend = []
          -580        timelegend = []
          -581        stdevlegend = []
          -582        whichchn = []
          -583        gains = []
          -584        toplotx = []
          -585        toploty = []
          -586        nactive = 0
          -587        def convert_pkg():
          -588            plttime = 0
          -589            if self.ignore_skew:
          -590                plttime = sum(pkg[0]) / len(pkg[0])
          -591            traceidx = 0
          -592            tmptime = []
          -593            tmpavg = []
          -594            tmpstd = []
          -595            tmpavg_std = []
          -596            for i, k in zip(self.tracemap, self.tracefrdatachn):
          -597
          -598                avg = pkg[1][k]
          -599                std = pkg[2][k]
          -600                avg_std = pkg[3][k]
          -601                avg_vdd = pkg[4][k]
          -602                avg, std, avg_std = self.traces[i].toselectedunits(avg,
          -603                                                                   std, avg_std,
          -604                                                                   avg_vdd)
          -605                avg, std, avg_std = sensors. \
          -606                    to_reasonable_significant_figures_fast(avg, std, avg_std)
          -607                tmptime.append(pkg[0][k])
          -608                tmpavg.append(avg)
          -609                tmpstd.append(std)
          -610                tmpavg_std.append(avg_std)
          -611                if self.ignore_skew:
          -612                    toplotx[traceidx].append(plttime)
          -613                else:
          -614                    toplotx[traceidx].append(pkg[0][k])
          -615                toploty[traceidx].append(avg)
          -616                traceidx += 1
          -617            timestamp.append(tmptime)
          -618            data.append(tmpavg)
          -619            stdev.append(tmpavg_std)
          -620            return
          -621
          -622        for k in self.traces:
          -623            if k.isactive:
          -624                nactive += 1
          -625        if self.separate_plots:
          -626            self.livefig.set_subplots(rows = nactive, cols = 1,
          -627                                      shared_xaxes= True)
          -628            self.livefig.update_xaxes(title = self.timelbl.value,
          -629                                      row = nactive, col = 1)
          -630        else:
          -631            self.livefig.update_yaxes(title = "Values")
          -632            self.livefig.update_xaxes(title = self.timelbl.value)
          -633        active_count = 0
          -634        for i in range(self.ntraces):
          -635            if (self.traces[i].isactive):
          -636                active_count += 1
          -637                tempstr = self.traces[i].tracelbl.value + '(' + \
          -638                          self.traces[i].units.value + ')'
          -639                timelegend.append('time_' + tempstr)
          -640                datalegend.append(tempstr)
          -641                stdevlegend.append('stdev_' + tempstr)
          -642                if self.separate_plots:
          -643                    scat = go.Scatter(y=[],x=[], name=tempstr)
          -644                    self.livefig.add_trace(scat, row = active_count,
          -645                                           col = 1)
          -646                    self.livefig.update_yaxes(title = self.traces[
          -647                        i].units.value, row = active_count, col = 1)
          -648                else:
          -649                    self.livefig.add_scatter(y=[],x=[], name=tempstr)
          -650                toplotx.append([])
          -651                toploty.append([])
          -652        lastupdatetime = time.time()
          -653
          -654        pts = 0
          -655        oldpts = 0
          -656        #print('about to enter while loop',end='')
          -657        while (self.collectbtn.description == 'Stop Collecting'):
          -658            #print('.',end='')
          -659            while PLTconn.poll():
          -660                pkg = PLTconn.recv()
          -661                self.lastpkgstr = str(pkg)
          -662                #print(self.lastpkgstr)
          -663                # convert voltage to requested units.
          -664                convert_pkg()
          -665            currenttime = time.time()
          -666            mindelay = 1.0
          -667            if self.separate_traces_checkbox.value:
          -668                mindelay = nactive*1.0
          -669            else:
          -670                mindelay = nactive*0.5
          -671            if (currenttime - lastupdatetime)>(mindelay+len(toplotx[0])*len(
          -672                    toplotx)/1000):
          -673                lastupdatetime = currenttime
          -674                for k in range(len(self.livefig.data)):
          -675                    self.livefig.data[k].x=toplotx[k]
          -676                    self.livefig.data[k].y=toploty[k]
          -677            #time.sleep(1)
          -678            PLTCTL.send('send')
          -679            time.sleep(self.delta)
          -680            # print ('btn.description='+str(btn.description))
          -681        endtime = time.time()
          -682        PLTCTL.send('stop')
          -683        time.sleep(0.5)  # wait 0.5 second to collect remaining data
          -684        PLTCTL.send('send')
          -685        time.sleep(0.5)
          -686        msg = ''
          -687        while (msg != 'done'):
          -688            while PLTconn.poll():
          -689                pkg = PLTconn.recv()
          -690                # print(str(pkg))
          -691                # convert voltage to requested units.
          -692                convert_pkg()
          -693            PLTCTL.send('send')
          -694            time.sleep(0.2)
          -695            if PLTCTL.poll():
          -696                msg = PLTCTL.recv()
          -697                # print (str(msg))
          -698                if (msg != 'done'):
          -699                    print('Received unexpected message: ' + str(msg))
          -700        for k in range(len(self.livefig.data)):
          -701            self.livefig.data[k].x = toplotx[k]
          -702            self.livefig.data[k].y = toploty[k]
          +            
          558    def updatingplot(self, PLTconn, PLTCTL):
          +559        """
          +560        Runs until a check of self.collectbtn.description does not return
          +561        'Stop Collecting'. This would probably be more efficient if set a
          +562        boolean.
          +563        Parameters
          +564        ----------
          +565        PLTconn: Pipe
          +566            connection plotter end
          +567        DAQconn: Pipe
          +568            connection DAQ end
          +569        PLTCTL: Pipe
          +570            control pipe plotter end
          +571        DAQCTL: Pipe
          +572            control pipe DAQ end
          +573        """
          +574        starttime = time.time()
          +575        global data
          +576        data = []
          +577        global timestamp
          +578        timestamp = []
          +579        global stdev
          +580        stdev = []
          +581        datalegend = []
          +582        timelegend = []
          +583        stdevlegend = []
          +584        whichchn = []
          +585        gains = []
          +586        toplotx = []
          +587        toploty = []
          +588        nactive = 0
          +589        def convert_pkg():
          +590            plttime = 0
          +591            if self.ignore_skew:
          +592                plttime = sum(pkg[0]) / len(pkg[0])
          +593            traceidx = 0
          +594            tmptime = []
          +595            tmpavg = []
          +596            tmpstd = []
          +597            tmpavg_std = []
          +598            for i, k in zip(self.tracemap, self.tracefrdatachn):
          +599
          +600                avg = pkg[1][k]
          +601                std = pkg[2][k]
          +602                avg_std = pkg[3][k]
          +603                avg_vdd = pkg[4][k]
          +604                avg, std, avg_std = self.traces[i].toselectedunits(avg,
          +605                                                                   std, avg_std,
          +606                                                                   avg_vdd)
          +607                avg, std, avg_std = sensors. \
          +608                    to_reasonable_significant_figures_fast(avg, std, avg_std)
          +609                tmptime.append(pkg[0][k])
          +610                tmpavg.append(avg)
          +611                tmpstd.append(std)
          +612                tmpavg_std.append(avg_std)
          +613                if self.ignore_skew:
          +614                    toplotx[traceidx].append(plttime)
          +615                else:
          +616                    toplotx[traceidx].append(pkg[0][k])
          +617                toploty[traceidx].append(avg)
          +618                traceidx += 1
          +619            timestamp.append(tmptime)
          +620            data.append(tmpavg)
          +621            stdev.append(tmpavg_std)
          +622            return
          +623
          +624        for k in self.traces:
          +625            if k.isactive:
          +626                nactive += 1
          +627        if self.separate_plots:
          +628            self.livefig.set_subplots(rows = nactive, cols = 1,
          +629                                      shared_xaxes= True)
          +630            self.livefig.update_xaxes(title = self.timelbl.value,
          +631                                      row = nactive, col = 1)
          +632        else:
          +633            self.livefig.update_yaxes(title = "Values")
          +634            self.livefig.update_xaxes(title = self.timelbl.value)
          +635        active_count = 0
          +636        for i in range(self.ntraces):
          +637            if (self.traces[i].isactive):
          +638                active_count += 1
          +639                tempstr = self.traces[i].tracelbl.value + '(' + \
          +640                          self.traces[i].units.value + ')'
          +641                timelegend.append('time_' + tempstr)
          +642                datalegend.append(tempstr)
          +643                stdevlegend.append('stdev_' + tempstr)
          +644                if self.separate_plots:
          +645                    scat = go.Scatter(y=[],x=[], name=tempstr)
          +646                    self.livefig.add_trace(scat, row = active_count,
          +647                                           col = 1)
          +648                    self.livefig.update_yaxes(title = self.traces[
          +649                        i].units.value, row = active_count, col = 1)
          +650                else:
          +651                    self.livefig.add_scatter(y=[],x=[], name=tempstr)
          +652                toplotx.append([])
          +653                toploty.append([])
          +654        lastupdatetime = time.time()
          +655
          +656        pts = 0
          +657        oldpts = 0
          +658        #print('about to enter while loop',end='')
          +659        while (self.collectbtn.description == 'Stop Collecting'):
          +660            #print('.',end='')
          +661            while PLTconn.poll():
          +662                pkg = PLTconn.recv()
          +663                self.lastpkgstr = str(pkg)
          +664                #print(self.lastpkgstr)
          +665                # convert voltage to requested units.
          +666                convert_pkg()
          +667            currenttime = time.time()
          +668            mindelay = 1.0
          +669            if self.separate_traces_checkbox.value:
          +670                mindelay = nactive*1.0
          +671            else:
          +672                mindelay = nactive*0.5
          +673            if (currenttime - lastupdatetime)>(mindelay+len(toplotx[0])*len(
          +674                    toplotx)/1000):
          +675                lastupdatetime = currenttime
          +676                for k in range(len(self.livefig.data)):
          +677                    self.livefig.data[k].x=toplotx[k]
          +678                    self.livefig.data[k].y=toploty[k]
          +679            #time.sleep(1)
          +680            PLTCTL.send('send')
          +681            time.sleep(self.delta)
          +682            # print ('btn.description='+str(btn.description))
          +683        endtime = time.time()
          +684        PLTCTL.send('stop')
          +685        time.sleep(0.5)  # wait 0.5 second to collect remaining data
          +686        PLTCTL.send('send')
          +687        time.sleep(0.5)
          +688        msg = ''
          +689        while (msg != 'done'):
          +690            while PLTconn.poll():
          +691                pkg = PLTconn.recv()
          +692                # print(str(pkg))
          +693                # convert voltage to requested units.
          +694                convert_pkg()
          +695            PLTCTL.send('send')
          +696            time.sleep(0.2)
          +697            if PLTCTL.poll():
          +698                msg = PLTCTL.recv()
          +699                # print (str(msg))
          +700                if (msg != 'done'):
          +701                    print('Received unexpected message: ' + str(msg))
          +702        for k in range(len(self.livefig.data)):
          +703            self.livefig.data[k].x = toplotx[k]
          +704            self.livefig.data[k].y = toploty[k]
          +705        return
           
          @@ -2121,40 +2128,40 @@

          Parameters

          -
          714def Run(name):
          -715    """Load a run from stored data or start a new run if the local file for
          -716    the run does not exist.
          -717    Parameters
          -718    ----------
          -719    name: str
          -720        String name for the run. The data will be stored in a file of this
          -721        name with the extension of `.jpidaq.html`.
          -722    """
          -723    from pathlib import Path
          -724    from IPython import get_ipython
          -725    from IPython.display import display
          -726    global_dict = get_ipython().user_ns
          -727    runs = None
          -728    if 'runs' in global_dict and 'DAQinstance' in global_dict:
          -729        runs = global_dict['runs']
          -730    else:
          -731        return ('Initialization of JupyterPiDAQ required')
          -732    # Check if run completed, if so reload data, display and exit
          -733    datafilepath = Path.cwd() / Path(str(name) + '.jpidaq.html')
          -734    if datafilepath.exists():
          -735        # display the data as a live plotly plot.
          -736        svname = name + '.jpidaq.html'
          -737        runs.append(DAQinstance(len(runs)+1, title = name))
          -738        runs[-1]._load_from_html(svname)
          -739        display(HTML(runs[-1].defaultparamtxt))
          -740        display(HTML('<h3>Saved as: '+runs[-1].svname+'</h3>'))
          -741        display(runs[-1].livefig)
          -742        display(HTML(runs[-1].defaultcollecttxt))
          -743        return
          -744    nrun = len(runs) + 1
          -745    runs.append(DAQinstance(nrun, title=name))
          -746    runs[-1].setup()
          -747    return
          +            
          717def Run(name):
          +718    """Load a run from stored data or start a new run if the local file for
          +719    the run does not exist.
          +720    Parameters
          +721    ----------
          +722    name: str
          +723        String name for the run. The data will be stored in a file of this
          +724        name with the extension of `.jpidaq.html`.
          +725    """
          +726    from pathlib import Path
          +727    from IPython import get_ipython
          +728    from IPython.display import display
          +729    global_dict = get_ipython().user_ns
          +730    runs = None
          +731    if 'runs' in global_dict and 'DAQinstance' in global_dict:
          +732        runs = global_dict['runs']
          +733    else:
          +734        return ('Initialization of JupyterPiDAQ required')
          +735    # Check if run completed, if so reload data, display and exit
          +736    datafilepath = Path.cwd() / Path(str(name) + '.jpidaq.html')
          +737    if datafilepath.exists():
          +738        # display the data as a live plotly plot.
          +739        svname = name + '.jpidaq.html'
          +740        runs.append(DAQinstance(len(runs)+1, title = name))
          +741        runs[-1]._load_from_html(svname)
          +742        display(HTML(runs[-1].defaultparamtxt))
          +743        display(HTML('<h3>Saved as: '+runs[-1].svname+'</h3>'))
          +744        display(runs[-1].livefig)
          +745        display(HTML(runs[-1].defaultcollecttxt))
          +746        return
          +747    nrun = len(runs) + 1
          +748    runs.append(DAQinstance(nrun, title=name))
          +749    runs[-1].setup()
          +750    return
           
          @@ -2181,16 +2188,16 @@

          Parameters

          -
          749def doRun(whichrun):
          -750    with whichrun.output:
          -751        display(HTML('<span id="LiveRun_'+str(whichrun.idno)+'"></span>'))
          -752        display(HTML(whichrun.defaultparamtxt))
          -753        if hasattr(whichrun, "collectbtn"):
          -754            # only show if hasn't already collected data
          -755            whichrun.collectbtn.on_click(whichrun.collectclick)
          -756            display(whichrun.collectbtn)
          -757        display(HTML(whichrun.defaultcollecttxt))
          -758    pass
          +            
          752def doRun(whichrun):
          +753    with whichrun.output:
          +754        display(HTML('<span id="LiveRun_'+str(whichrun.idno)+'"></span>'))
          +755        display(HTML(whichrun.defaultparamtxt))
          +756        if hasattr(whichrun, "collectbtn"):
          +757            # only show if hasn't already collected data
          +758            whichrun.collectbtn.on_click(whichrun.collectclick)
          +759            display(whichrun.collectbtn)
          +760        display(HTML(whichrun.defaultcollecttxt))
          +761    pass
           
          @@ -2208,20 +2215,20 @@

          Parameters

          -
          817def update_runsdrp():
          -818    # get list of runs
          -819    runlst = [('Choose Run', -1)]
          -820    for i in range(len(runs)):
          -821        runlst.append((str(i + 1) + ': ' + runs[i].title, i))
          -822    # buid selection menu
          -823    global runsdrp
          -824    runsdrp = widgets.Dropdown(
          -825        options=runlst,
          -826        value=-1,
          -827        description='Select Run #:',
          -828        disabled=False,
          -829    )
          -830    pass
          +            
          820def update_runsdrp():
          +821    # get list of runs
          +822    runlst = [('Choose Run', -1)]
          +823    for i in range(len(runs)):
          +824        runlst.append((str(i + 1) + ': ' + runs[i].title, i))
          +825    # buid selection menu
          +826    global runsdrp
          +827    runsdrp = widgets.Dropdown(
          +828        options=runlst,
          +829        value=-1,
          +830        description='Select Run #:',
          +831        disabled=False,
          +832    )
          +833    pass
           
          @@ -2239,17 +2246,17 @@

          Parameters

          -
          832def showSelectedRunTable(change):
          -833    global runsdrp
          -834    global last_run_table_out
          -835    whichrun = runsdrp.value
          -836    runsdrp.close()
          -837    last_run_table_out.clear_output()
          -838    tbldiv = '<div style="height:10em;">' + str(runs[whichrun].title)
          -839    tbldiv += str(runs[whichrun].pandadf.to_html()) + '</div>'
          -840    with last_run_table_out:
          -841        display(HTML(tbldiv))
          -842    return
          +            
          835def showSelectedRunTable(change):
          +836    global runsdrp
          +837    global last_run_table_out
          +838    whichrun = runsdrp.value
          +839    runsdrp.close()
          +840    last_run_table_out.clear_output()
          +841    tbldiv = '<div style="height:10em;">' + str(runs[whichrun].title)
          +842    tbldiv += str(runs[whichrun].pandadf.to_html()) + '</div>'
          +843    with last_run_table_out:
          +844        display(HTML(tbldiv))
          +845    return
           
          @@ -2267,23 +2274,23 @@

          Parameters

          -
          844def showDataTable():
          -845    """
          -846    Provides a menu to select which run. Then displays the run in a
          -847    10 em high scrolling table. Selection menu is removed after choice
          -848    is made.
          -849    """
          -850    from ipywidgets import Output
          -851    global last_run_table_out
          -852    last_run_table_out = Output()
          -853    update_runsdrp()
          -854    global runsdrp
          -855    runsdrp.observe(showSelectedRunTable, names='value')
          -856    with last_run_table_out:
          -857        display(runsdrp)
          -858    display(last_run_table_out)
          -859    # will display selected run and delete menu upon selection.
          -860    return
          +            
          847def showDataTable():
          +848    """
          +849    Provides a menu to select which run. Then displays the run in a
          +850    10 em high scrolling table. Selection menu is removed after choice
          +851    is made.
          +852    """
          +853    from ipywidgets import Output
          +854    global last_run_table_out
          +855    last_run_table_out = Output()
          +856    update_runsdrp()
          +857    global runsdrp
          +858    runsdrp.observe(showSelectedRunTable, names='value')
          +859    with last_run_table_out:
          +860        display(runsdrp)
          +861    display(last_run_table_out)
          +862    # will display selected run and delete menu upon selection.
          +863    return
           
          @@ -2305,17 +2312,17 @@

          Parameters

          -
          861def newCalculatedColumn():
          -862    """
          -863    Uses jupyter-pandas-GUI.new_pandas_column_GUI to provide a GUI expression
          -864    composer. This method finds the datasets and launches the GUI.
          -865    """
          -866    df_info = []
          -867    for i in range(len(runs)):
          -868        df_info.append([runs[i].pandadf, 'runs['+str(i)+'].pandadf',
          -869                        str(runs[i].title)])
          -870    new_pandas_column_GUI(df_info)
          -871    pass
          +            
          864def newCalculatedColumn():
          +865    """
          +866    Uses jupyter-pandas-GUI.new_pandas_column_GUI to provide a GUI expression
          +867    composer. This method finds the datasets and launches the GUI.
          +868    """
          +869    df_info = []
          +870    for i in range(len(runs)):
          +871        df_info.append([runs[i].pandadf, 'runs['+str(i)+'].pandadf',
          +872                        str(runs[i].title)])
          +873    new_pandas_column_GUI(df_info)
          +874    pass
           
          @@ -2336,18 +2343,18 @@

          Parameters

          -
          873def newPlot():
          -874    """
          -875    Uses jupyter-pandas-GUI.plot_pandas_GUI to provide a GUI expression
          -876    composer. This method finds the datasets and launches the GUI.
          -877    """
          -878    df_info = []
          -879    for i in range(len(runs)):
          -880        if isinstance(runs[i].pandadf,pd.DataFrame):
          -881            df_info.append([runs[i].pandadf, 'runs['+str(i)+'].pandadf',
          -882                            str(runs[i].title)])
          -883    plot_pandas_GUI(df_info)
          -884    pass
          +            
          876def newPlot():
          +877    """
          +878    Uses jupyter-pandas-GUI.plot_pandas_GUI to provide a GUI expression
          +879    composer. This method finds the datasets and launches the GUI.
          +880    """
          +881    df_info = []
          +882    for i in range(len(runs)):
          +883        if isinstance(runs[i].pandadf,pd.DataFrame):
          +884            df_info.append([runs[i].pandadf, 'runs['+str(i)+'].pandadf',
          +885                            str(runs[i].title)])
          +886    plot_pandas_GUI(df_info)
          +887    pass
           
          @@ -2368,18 +2375,18 @@

          Parameters

          -
          886def newFit():
          -887    """
          -888    Uses jupyter-pandas-GUI.fit_pandas_GUI to provide a GUI expression
          -889    composer. This method finds the datasets and launches the GUI.
          -890    """
          -891    df_info = []
          -892    for i in range(len(runs)):
          -893        if isinstance(runs[i].pandadf,pd.DataFrame):
          -894            df_info.append([runs[i].pandadf, 'runs['+str(i)+'].pandadf',
          -895                            str(runs[i].title)])
          -896    fit_pandas_GUI(df_info)
          -897    pass
          +            
          889def newFit():
          +890    """
          +891    Uses jupyter-pandas-GUI.fit_pandas_GUI to provide a GUI expression
          +892    composer. This method finds the datasets and launches the GUI.
          +893    """
          +894    df_info = []
          +895    for i in range(len(runs)):
          +896        if isinstance(runs[i].pandadf,pd.DataFrame):
          +897            df_info.append([runs[i].pandadf, 'runs['+str(i)+'].pandadf',
          +898                            str(runs[i].title)])
          +899    fit_pandas_GUI(df_info)
          +900    pass
           
          diff --git a/docs/jupyterpidaq/Sensors.html b/docs/jupyterpidaq/Sensors.html index 0eebef1..e81ee37 100644 --- a/docs/jupyterpidaq/Sensors.html +++ b/docs/jupyterpidaq/Sensors.html @@ -3,7 +3,7 @@ - + jupyterpidaq.Sensors API documentation @@ -34,7 +34,7 @@

          Submodules

          -
          JupyterPiDAQ v0.8.0
          +
          JupyterPiDAQ v0.8.1
          built with pdoc - + jupyterpidaq.Sensors.sensors API documentation @@ -181,7 +181,7 @@

          API Documentation

          -
          JupyterPiDAQ v0.8.0
          +
          JupyterPiDAQ v0.8.1
          built with pdoco;o++){for(var r=e[o],s=0;i>s&&(r=this._queue[s](r,o,e),void 0!==r&&null!==r);s++);void 0!==r&&null!==r&&t.push(r)}return t},t.Pipeline.prototype.reset=function(){this._queue=[]},t.Pipeline.prototype.get=function(){return this._queue},t.Pipeline.prototype.toJSON=function(){return this._queue.map(function(e){return t.Pipeline.warnIfFunctionNotRegistered(e),e.label})},t.Index=function(){this._fields=[],this._ref="id",this.pipeline=new t.Pipeline,this.documentStore=new t.DocumentStore,this.index={},this.eventEmitter=new t.EventEmitter,this._idfCache={},this.on("add","remove","update",function(){this._idfCache={}}.bind(this))},t.Index.prototype.on=function(){var e=Array.prototype.slice.call(arguments);return this.eventEmitter.addListener.apply(this.eventEmitter,e)},t.Index.prototype.off=function(e,t){return this.eventEmitter.removeListener(e,t)},t.Index.load=function(e){e.version!==t.version&&t.utils.warn("version mismatch: current "+t.version+" importing "+e.version);var n=new this;n._fields=e.fields,n._ref=e.ref,n.documentStore=t.DocumentStore.load(e.documentStore),n.pipeline=t.Pipeline.load(e.pipeline),n.index={};for(var i in e.index)n.index[i]=t.InvertedIndex.load(e.index[i]);return n},t.Index.prototype.addField=function(e){return this._fields.push(e),this.index[e]=new t.InvertedIndex,this},t.Index.prototype.setRef=function(e){return this._ref=e,this},t.Index.prototype.saveDocument=function(e){return this.documentStore=new t.DocumentStore(e),this},t.Index.prototype.addDoc=function(e,n){if(e){var n=void 0===n?!0:n,i=e[this._ref];this.documentStore.addDoc(i,e),this._fields.forEach(function(n){var o=this.pipeline.run(t.tokenizer(e[n]));this.documentStore.addFieldLength(i,n,o.length);var r={};o.forEach(function(e){e in r?r[e]+=1:r[e]=1},this);for(var s in r){var u=r[s];u=Math.sqrt(u),this.index[n].addToken(s,{ref:i,tf:u})}},this),n&&this.eventEmitter.emit("add",e,this)}},t.Index.prototype.removeDocByRef=function(e){if(e&&this.documentStore.isDocStored()!==!1&&this.documentStore.hasDoc(e)){var t=this.documentStore.getDoc(e);this.removeDoc(t,!1)}},t.Index.prototype.removeDoc=function(e,n){if(e){var n=void 0===n?!0:n,i=e[this._ref];this.documentStore.hasDoc(i)&&(this.documentStore.removeDoc(i),this._fields.forEach(function(n){var o=this.pipeline.run(t.tokenizer(e[n]));o.forEach(function(e){this.index[n].removeToken(e,i)},this)},this),n&&this.eventEmitter.emit("remove",e,this))}},t.Index.prototype.updateDoc=function(e,t){var t=void 0===t?!0:t;this.removeDocByRef(e[this._ref],!1),this.addDoc(e,!1),t&&this.eventEmitter.emit("update",e,this)},t.Index.prototype.idf=function(e,t){var n="@"+t+"/"+e;if(Object.prototype.hasOwnProperty.call(this._idfCache,n))return this._idfCache[n];var i=this.index[t].getDocFreq(e),o=1+Math.log(this.documentStore.length/(i+1));return this._idfCache[n]=o,o},t.Index.prototype.getFields=function(){return this._fields.slice()},t.Index.prototype.search=function(e,n){if(!e)return[];e="string"==typeof e?{any:e}:JSON.parse(JSON.stringify(e));var i=null;null!=n&&(i=JSON.stringify(n));for(var o=new t.Configuration(i,this.getFields()).get(),r={},s=Object.keys(e),u=0;u0&&t.push(e);for(var i in n)"docs"!==i&&"df"!==i&&this.expandToken(e+i,t,n[i]);return t},t.InvertedIndex.prototype.toJSON=function(){return{root:this.root}},t.Configuration=function(e,n){var e=e||"";if(void 0==n||null==n)throw new Error("fields should not be null");this.config={};var i;try{i=JSON.parse(e),this.buildUserConfig(i,n)}catch(o){t.utils.warn("user configuration parse failed, will use default configuration"),this.buildDefaultConfig(n)}},t.Configuration.prototype.buildDefaultConfig=function(e){this.reset(),e.forEach(function(e){this.config[e]={boost:1,bool:"OR",expand:!1}},this)},t.Configuration.prototype.buildUserConfig=function(e,n){var i="OR",o=!1;if(this.reset(),"bool"in e&&(i=e.bool||i),"expand"in e&&(o=e.expand||o),"fields"in e)for(var r in e.fields)if(n.indexOf(r)>-1){var s=e.fields[r],u=o;void 0!=s.expand&&(u=s.expand),this.config[r]={boost:s.boost||0===s.boost?s.boost:1,bool:s.bool||i,expand:u}}else t.utils.warn("field name in user configuration not found in index instance fields");else this.addAllFields2UserConfig(i,o,n)},t.Configuration.prototype.addAllFields2UserConfig=function(e,t,n){n.forEach(function(n){this.config[n]={boost:1,bool:e,expand:t}},this)},t.Configuration.prototype.get=function(){return this.config},t.Configuration.prototype.reset=function(){this.config={}},lunr.SortedSet=function(){this.length=0,this.elements=[]},lunr.SortedSet.load=function(e){var t=new this;return t.elements=e,t.length=e.length,t},lunr.SortedSet.prototype.add=function(){var e,t;for(e=0;e1;){if(r===e)return o;e>r&&(t=o),r>e&&(n=o),i=n-t,o=t+Math.floor(i/2),r=this.elements[o]}return r===e?o:-1},lunr.SortedSet.prototype.locationFor=function(e){for(var t=0,n=this.elements.length,i=n-t,o=t+Math.floor(i/2),r=this.elements[o];i>1;)e>r&&(t=o),r>e&&(n=o),i=n-t,o=t+Math.floor(i/2),r=this.elements[o];return r>e?o:e>r?o+1:void 0},lunr.SortedSet.prototype.intersect=function(e){for(var t=new lunr.SortedSet,n=0,i=0,o=this.length,r=e.length,s=this.elements,u=e.elements;;){if(n>o-1||i>r-1)break;s[n]!==u[i]?s[n]u[i]&&i++:(t.add(s[n]),n++,i++)}return t},lunr.SortedSet.prototype.clone=function(){var e=new lunr.SortedSet;return e.elements=this.toArray(),e.length=e.elements.length,e},lunr.SortedSet.prototype.union=function(e){var t,n,i;this.length>=e.length?(t=this,n=e):(t=e,n=this),i=t.clone();for(var o=0,r=n.toArray();oUser Guide

          \n\n

          Introduction | \nUsage

          \n\n

          Introduction

          \n\n

          This software allows GUI (Graphical User Interface) driven live collection, \nplotting and analysis of digitized data inside a Jupyter notebook using the \nfollowing A-to-D interfaces:

          \n\n

          on a Raspberry Pi:

          \n\n\n\n

          on MacOS and Windows:

          \n\n\n\n

          demo mode on anything Jupyter runs on

          \n\n
            \n
          • A demo mode will run on any computer with a Jupyter notebook install and\nPython 3.6+. Example notebooks can be found in the \"usage_examples\" folder.
          • \n
          \n\n

          Usage

          \n\n

          Launch the software | \nInitialize data acquisiton tools | \nCollect data |\nDisplaying a Data Table | \nPlotting data | \nAnalyzing data

          \n\n

          Starting JupyterPiDAQ

          \n\n

          A working Jupyter notebook or Jupyter lab installation with JupyterPiDAQ \ninstalled is required. If you need to install the software see the Installation \nInstructions. There are two common ways this may be set \nup, that lead to slightly different steps for starting the software:

          \n\n
            \n
          1. A special kernel may be set up that can be used in any Jupyter notebook \ninstall for the current user (see the very end of the\nInstallation Instructions). \n
              \n
            • In this case launch\nJupyter, in whichever directory you want to work, using the \ncommand: jupyter notebook or jupyter lab.
            • \n
            • Open a new notebook and choose the kernel \nfor JupyterPiDAQ. The kernel name will depend upon what was chosen \nduring installation.
            • \n
          2. \n
          3. Only for use within the directory structure of the virtual environment \nthat was set up for the software. \n
              \n
            • In this case you must navigate to the \ndirectory of the virtual environment using the cd command before \nstarting the software.
            • \n
            • Then enter the virtual environment with the command pipenv shell. \nThis assumes you set up pipenv as described in the \nInstallation instructions.
            • \n
            • Launch Jupyter using the command: jupyter notebook or jupyter lab.
            • \n
            • Open a new python notebook.
            • \n
          4. \n
          \n\n

          Initialize the Data Acquisition Tools

          \n\n

          Initialize the data acquisition tools by putting the statement from \njupyterpidaq.DAQinstance import * into the first cell and clicking on the \n'Run' button. The package loads supporting packages (numpy, pandas, plotly,\netc...) and searches for compatible hardware. On a Raspberry Pi this takes a\nnumber of seconds. If no compatible A-to-D boards are found, demo mode is used.\nIn demo mode the A-to-D board is simulated by a random number generator.

          \n\n

          When setup is done a new menu appears at the end of the menubar (figure 1).

          \n\n

          \"DAQ

          \n\n

          Figure 1: The menu created once the data acquisition software is \ninitialized. Currently NOT available in Jupyter Lab.

          \n\n

          The menu options insert jupyter widget based GUIs for starting a run,\ndisplaying the data as tables or plots, composing an expression to calculate\na new column in a DataFrame, or fitting data.

          \n\n

          Collecting data

          \n\n

          Using the Menu

          \n\n

          From the \"DAQ Commands\" menu select \"Insert New Run After Selection...\" or \n\"Append New Run to End...\". The first will insert the code to set up \ndata collection for a run in the cell below the currently selected cell. \nThe second will append this to the end of the notebook. When the cell is \nrun it will generate \na GUI that looks like the figure 2, below. Fill in the information to define \nwhat you wish to do (see below figure 2 for more details).

          \n\n

          Using a Command

          \n\n

          In the cell you wish to collect the data enter the command \nRun(\"desired_dataset_name\"), where you replace desired_dataset_name with the \nstring you want for this run. Replacing spaces with _ will prevent issues, \nespecially on Windows. After entering the command, run the cell. This will \ngenerate the GUI shown in figure 2.

          \n\n

          \"New

          \n\n

          Figure 2: Image of the GUI for setting up a data collection run.

          \n\n
            \n
          1. Give the run a title/name using the first textbox.
          2. \n
          3. You can collect up to four (4) data traces at once. Two different data \ntraces can display the same analog-to-digital channel, but in different \nunits (e.g. you could record both the raw voltage and temperature from a \nthermistor). Each trace is activated by selecting its checkbox.
          4. \n
          5. Once a trace is activated you can give it a title and must select: the \ndata acquisition board, channel, sensor, units and gain from the \navailable drop-down menus.
          6. \n
          7. Once all your traces are set up decide whether they should be displayed in \nmultiple stacked graphs or all on the same graph. Uncheck the box to \ndisplay all on one graph.
          8. \n
          9. Select the data collection rate (20 Hz is currently the maximum rate, but \non a Raspberry Pi you may not be able to sustain more than 5 Hz).
          10. \n
          11. When everything is set the way you wish, click on the \"Set Parameters\" \nbutton. The collection parameters will be displayed and a button to \nstart the data collection will appear. You may have to scroll back up to \nthe display as Jupyter sometimes jumps too far down when a cell is updated.
          12. \n
          13. The \"start\" button will convert to a \"stop\" button once data collection \nis started. The data graph(s) will update at roughly 1 Hz, so you can \nmonitor the progress of the data collection.
          14. \n
          15. Click the \"stop\" button to end data collection. It can take \na while to stop if the data collection has got ahead of the graphic \ndisplay of the data.
          16. \n
          17. Once collection is stopped you will see a plot or plots of the completed \ndata collection and the name of the .html file the raw data has been \nbacked up to.
          18. \n
          19. Should you accidentally clear the output of a completed collection cell,\nrerunning it will regenerate the display as long as you have not moved \nthe raw data backup file.
          20. \n
          \n\n

          Displaying data

          \n\n

          In a table

          \n\n

          Selecting the \"Show data in table...\" option in the \"DAQ Command\" menu will \ninsert a cell immediately below the currently selected cell displaying a \nwidget in which you can select which data set to display. The command \nequivalent is showDataTable().

          \n\n

          Plotting data

          \n\n

          Selecting the \"Insert new plot after selection...\" option in the menu will\ninsert two cells immediately below the currently selected cell. The first cell\nwill be used to generate a GUI to lead you through creation of the code to\ngenerate the plot. The second cell is where the plot creation code is \ngenerated. The first tab of this GUI looks like figure 3. The command \nequivalent is newPlot().

          \n\n

          \"Plot

          \n\n

          Figure 3: Image of the first tab in the four tab (4 step) Pandas Plot \nComposer. More information in the Pandas_GUI\ndocumentation.

          \n\n

          It is best to do the tabs in order. The notices in red will try to \nwarn you of errors or oversights. The \"Instructions\" accordian can be \nexpanded to get more specific information about how to use each tab.

          \n\n

          You can get more sophisticated control of \nyour plot by editing the code produced by this GUI. See the Plotly \nFigureWidget Instructions and the \nexample Jupyter notebooks referenced there for more information.

          \n\n

          The GUI destroys itself once you complete step 4.

          \n\n

          Analyzing data

          \n\n

          Calculating a new column

          \n\n

          Selecting \"Calculate new column...\" from the menu will add two cells\nimmediately below the selected cell. The first cell will create the GUI to\nlead you through creation of the code to calculate the new column. The second\ncell is where the code is built. The first tab of the GUI looks like \nfigure 4. The command equivalent is newCalculatedColumn().

          \n\n

          \"New

          \n\n

          Figure 4: Image of the first tab in the four tab (4 step) Pandas New \nCalculated Column Composer. More information in the Pandas_GUI\ndocumentation.

          \n\n

          Do the tabs in order. You can perform more complex manipulations than built \ninto the GUI by editing the code generated by this GUI.

          \n\n

          The GUI destroys itself once you complete step 4.

          \n\n

          Fitting data

          \n\n

          A GUI for defining simple fits (linear, polynomial, exponential decay, sine \nand Gaussian) can be launched by selecting \"Insert new Fit after selection..\n.\" from the menu. This will create the GUI to lead you through selecting \nand fitting the data. The code is created in the cell immediately below the \nGUI. The command equivalent is newFit().

          \n\n

          \"Fit

          \n\n

          Figure 5: Image of the first tab in the Pandas Fit Composer.\nMore information in the Pandas_GUI\ndocumentation.

          \n\n

          Installation

          \n\n

          Initial setup: On Raspberry Pi | \nOn non-Pi Systems

          \n\n

          Final Set up

          \n\n

          Raspberry Pi Initial Setup

          \n\n

          Unless you only want to run in Demo mode make sure you have one of the \ncompatible interface boards installed. The current options are:

          \n\n
            \n
          • Adafruit compliant ADS1115 boards \n(example,\nalso available from other vendors);
          • \n
          • The π-Plates DAQC2 plate.
          • \n
          • If you wish to use different interfaces see the Development\nNotes\n and the Boards subpackage of jupyterpidaq for examples and \n information on how to define the code interface for a board.
          • \n
          \n\n

          OS specific: Ubuntu on Pi | \nRaspberrian on Pi |\nMacOS | Windows

          \n\n

          Ubuntu on Pi

          \n\n

          By default in Ubuntu 20.04 for Pis the gpio and spi groups do not exist.\nThe i2c group does (not always).

          \n\n
            \n
          1. Make sure that the following packages are installed rpi.gpio-common \npython3-pigpio python3-gpiozero python3-rpi.gpio.
          2. \n
          3. You can avoid having to create a gpio group, by assigning users who need\n gpio access to the dialout group. Check that /dev/gpiomem is part of that \ngroup and that the dialout group has rw access. If not you will need to set\n it.
          4. \n
          5. Users also need to be members of the i2c group. If it does not exist create \n it and then make that the group for /dev/i2c-1 with group rw permissions. \nTHIS MAY NOT BE NECESSARY.
          6. \n
          7. The spi group needs to be created (addgroup?).
          8. \n
          9. Additionally the spi group needs to be given rw access to the spi devices\nat each boot. To do this create a one line rule in a file named \n/etc/udev/rules.d/50-spidev.rules containing SUBSYSTEM==\"spidev\", \nGROUP=\"spi\", MODE=\"0660\". The file should have rw permission for root \nand read permission for everyone else.
          10. \n
          11. Make sure you have pip installed for \npython 3: python3 -m pip --version or pip3 --version. If you do not, \ninstall using apt \ninstall python3-pip.
          12. \n
          \n\n

          Raspberrian on Pi

          \n\n

          (TBD)

          \n\n

          Non-Pi based System Initial Setup

          \n\n

          Make sure that Python >=3.6 is installed: python3 -v. If not follow \ninstructions at python.org. This software should run \non any computer capable of supporting the necessary version of Python. \nHowevever, it will only run in demo mode if the computer does not support \none of the compatible A-to-D boards.

          \n\n

          Generic Linux

          \n\n
            \n
          • If your system hardware \nhas GPIO pins and a GPIO interface board, you should try following the \ninstructions for a Pi based system above. If \nyou figure out how to make this work on other SBCs or systems with GPIO, \nplease submit a pull request updating these instructions.
          • \n
          • If your system hardware does not support GPIO and one of the compatible \ninterface boards, the software will run in demo mode.
          • \n
          \n\n

          NOTE: If a binary distribution (whl or wheel) is not available for your\nplatform, some of the required packages may need to be compiled. If you get\ncompilation errors when installing try getting the python header and \ndevelopment files for your platform. To get them on most *nix platforms use the\ncommand sudo apt install python3-dev.

          \n\n

          MacOS

          \n\n

          Currently, the LabQuest A-to-Ds are only working on intel based macs. When \nVernier recompiles their interface communication library for the M1 series \ncpus this will change.

          \n\n
            \n
          1. Install the latest version of Python, by following the instructions on \nthe Python website.
          2. \n
          3. Make sure a virtual environment tool such as pipenv is installed. In a \nterminal window try python -m pip install --user pipenv.
          4. \n
          \n\n

          Windows

          \n\n

          Note: LoggerPro software may need to be \ninstalled because that provides the compiled .dll necessary to communicate \nwith the LabQuest interface. In theory will work without this.

          \n\n
            \n
          1. Install the latest version of Python, by following the instructions on \nthe Python website. WARNING: Do Not use \nWindows automatic installation. It may work on a plain vanilla system \nwith one user, but in multiuser systems it badly messes up the ability to \nuse virtual environments.
          2. \n
          3. Make sure a virtual environment tool such as pipenv is installed. In a \nterminal window try python -m pip install --user pipenv.
          4. \n
          5. You may have to set or add to your user PATH environment variable. See \nthe warnings generated during the installation. Then search for the \nlatest instructions on how to set PATH.
          6. \n
          \n\n

          Final Set Up

          \n\n
            \n
          • If on Raspberry Pi type system, make sure the user you will be running the \nsoftware under is a member of the groups dialout, spi and if it \nexistsi2c.
          • \n
          • It is recommended that you install JupyterPiDAQ in its own \nvirtual environment.\nThe instructions below do just that using the original author's favorite \nvirtual environment tool pipenv.
          • \n
          \n\n

          Log into your chosen user account:

          \n\n
            \n
          1. Install pipenv: pip3 install \n--user pipenv. You may\nneed to add ~/.local/bin to your PATH to make pipenv\navailable in your command shell. More discussion: \nThe Hitchhiker's Guide to\nPython.
          2. \n
          3. Create a directory for the virtual environment you will be installing\ninto (example: mkdir JupyterPiDAQ).
          4. \n
          5. Navigate into the directory cd JupyterPiDAQ.
          6. \n
          7. Create the virtual environment and enter it pipenv shell. To get out of\nthe environment you can issue the exit command on the command line.
          8. \n
          9. While still in the shell install the latest JupyterPiDAQ and all its\nrequirements\n pip install -U JupyterPiDAQ. This can take a long time, especially on a\n Raspberry Pi. On a Pi 3B+ (minimum requirement) it will probably not run\n without at least 1 GB of swap. See: Build Jupyter on a Pi\n for a discussion of adding swap space on a Pi.
          10. \n
          11. Still within the environment shell test\nthis by starting jupyter jupyter notebook or jupyter lab. Jupyter should \nlaunch in your browser.\n
              \n
            1. Open a new notebook using the default (Python 3) kernel.
            2. \n
            3. In the first cell import all from DAQinstance.py: \nfrom jupyterpidaq.DAQinstance import *.\n When run in jupyter notebook this cell should load the DAQmenu at the \n end of the Jupyter notebook menu/icon bar. Currently, no convenience \n menu is available in Jupyter lab. If you do not have an appropriate\n A-to-D board installed you will get a message and the software\n will default to demo mode, substituting a random number\n generator for the A-to-D.
            4. \n
          12. \n
          13. If you wish, you can make this environment available to an alternate Jupyter\ninstall as a special kernel when you are the user.\n
              \n
            1. Make sure you are running in your virtual environment pipenv shell \nin the directory for virtual environment will do that.
            2. \n
            3. Issue the command to add this as a kernel to your personal space: \npython -m ipykernel install --user --name=<name-you-want-for-kernel>.
            4. \n
            5. More information is available in the Jupyter/IPython documentation. \nA simple tutorial from Nikolai Jankiev (_Parametric Thoughts_) can be\nfound here.
            6. \n
          14. \n
          \n\n

          Development Notes

          \n\n

          Code Repository

          \n\n

          Setting up Development Environment

          \n\n

          Basic requirements: Python 3.6+, associated\npip and a Jupyter notebook.\nSee: python.org and\nJupyter.org.

          \n\n
            \n
          1. If not installed, install pipenv:$ pip3 install --user pipenv. You may\nneed to add ~/.local/bin to your PATH to make pipenv\navailable in your command shell. More discussion: \nThe Hitchhiker's Guide to Python.
          2. \n
          3. Navigate to the directory where this package will be\nor has been downloaded to. Use pipenvto install an \n\"editable\" package \ninside the directory as described below:\n
              \n
            1. Start a shell in the environment $ pipenv shell.
            2. \n
            3. Install using pip.\n
                \n
              1. If you downloaded the git repository named \"JupyterPiDAQ\"\nand have used that directory to build your virtual\nenvironment: $ pip install -e ../JupyterPiDAQ/.
              2. \n
              3. If you are downloading from PyPi\n$ pip install -e JupyterPiDAQ
              4. \n
              5. Either should install all the additional packages this\npackage depends upon. On a Raspberry Pi this will take\na long time. It probably will not run without at least 1 GB of swap. See: \nBuild Jupyter on a Pi\n.
              6. \n
            4. \n
            5. Still within the environment shell test\nthis by starting jupyter $ jupyter notebook. Jupyter should launch in\nyour browser.\n
                \n
              1. Open a new notebook using the default (Python 3) kernel.
              2. \n
              3. In the first cell import all from DAQinstance.py: \nfrom jupyterpidaq.DAQinstance import *.\nWhen run this cell should load the DAQmenu at the end of the\nJupyter notebook menu/icon bar. If you do not have an appropriate A-to-D\nboard installed you will get a message and the software\nwill default to demo mode, substituting a random number\ngenerator for the A-to-D. Because of the demo mode it is\npossible to run this on any computer, not just a Pi.
              4. \n
            6. \n
          4. \n
          5. If you wish, you can make this environment available to an alternate Jupyter\ninstall as a special kernel when you are the user.\n
              \n
            1. Make sure you are running in your virtual environment $ pipenv shell \nin the directory for virtual environment will do that.
            2. \n
            3. Issue the command to add this as a kernel to your personal space: \n$ python -m ipykernel install --user --name=<name-you-want-for-kernel>.
            4. \n
            5. More information is available in the Jupyter/Ipython documentation. \nA simple tutorial from Nikolai Jankiev (_Parametric Thoughts_) can be\nfound here.
            6. \n
          6. \n
          \n\n

          Adding New Sensor Code

          \n\n
            \n
          1. Copy an existing sensor class paste it into the end of\nsensors.py and rename it.
          2. \n
          3. Update/delete functions for each valid unit within the new\nclass as necessary.
          4. \n
          5. Update the sensor name, vendor and available units in the\n__init__ function.
          6. \n
          7. Add the new sensor classname to the list of available sensors\nin listSensors at about line 120 of sensors.py.
          8. \n
          9. Add the new sensor classname to getsensors of ADCsim.py,\nADCsim_line.py and any board (e.g. DAQC2.py) with which the sensor\ncan be used. Do not guess if a sensor works with a particular\nboard. Test it!
          10. \n
          \n\n

          Running Tests

          \n\n
            \n
          1. Install updated pytest in the virtual environment:\n
            pipenv shell\npip install -U pytest\n
          2. \n
          3. Run tests ignoring the manual tests in the dev_testing directory:\npython -m pytest --ignore='dev_testing'.
          4. \n
          \n\n

          Building Documentation

          \n\n
            \n
          1. Install or update pdoc into the virtual environment pip install -U pdoc.
          2. \n
          3. Make edits to the .md files within the docs folder that are to be \nincluded in the first page (see __init__.py of the jupyterpidaq package).
          4. \n
          5. At the root level run pdoc \n--logo https://jupyterphysscilab.github.io/JupyterPiDAQ/JupyterPiDAQ-logo.svg --logo-link \nhttps://jupyterphysscilab.github.io/JupyterPiDAQ/ --footer-text \n\"JupyterPiDAQ vX.X.X\" -html -o docs jupyterpidaq Unless you are on a \nRaspbery Pi this will throw an error about import. Just ignore.
          6. \n
          \n\n

          Building PyPi package

          \n\n
            \n
          1. Make sure to update the version number in setup.py first.
          2. \n
          3. Install updated setuptools and twine in the virtual environment:\n
            pipenv shell\npip install -U setuptools wheel twine\n
          4. \n
          5. Build the distribution python -m setup sdist bdist_wheel.
          6. \n
          7. Test it on test.pypi.org.\n
              \n
            1. Upload it (you will need an account on test.pypi.org):\npython -m twine upload --repository testpypi dist/*.
            2. \n
            3. Create a new virtual environment and test install into it:\n
              exit # to get out of the current environment\ncd <somewhere>\nmkdir <new virtual environment>\ncd <new directory>\npipenv shell #creates the new environment and enters it.\npip install -i https://test.pypi.org/..... # copy actual link from the\n                                           # repository on test.pypi.\n
              \nThere are often install issues because sometimes only older versions of\nsome of the required packages are available on test.pypi.org. If this\nis the only problem change the version to end in rc0 for release\ncandidate and try it on the regular pypi.org as described below for\nreleasing on PyPi.
            4. \n
            5. After install test by running a jupyter notebook in the virtual \nenvironment.
            6. \n
          8. \n
          \n\n

          Releasing on PyPi

          \n\n

          Proceed only if testing of the build is successful.

          \n\n
            \n
          1. Double check the version number in setup.py.
          2. \n
          3. Rebuild the release: python -m setup sdist bdist_wheel.
          4. \n
          5. Upload it: python -m twine upload dist/*
          6. \n
          7. Make sure it works by installing it in a clean virtual environment. This\nis the same as on test.pypi.org except without -i https://test.pypy.... If\nit does not work, pull the release.
          8. \n
          \n\n

          Change Log

          \n\n
            \n
          • 0.8.0 (Apr. 20, 2023)\n
              \n
            • Replaced NewRun() command with Run() command. This version works in \nJupyter Lab and removes the need for the DisplayRun() command because \nRun() will load an already collected dataset or start a new one.
            • \n
            • Now, when multiple traces are assigned to the same channel of a board the \nchannel is only read one time. If the units are different the two \ntraces will be displayed with different units.
            • \n
            • Can read data from Vernier LabQuest USB \nanalog-to-digital interfaces. Potential data rate is 10 kHz, currently \nlimited to 20 Hz.
            • \n
            • Runs in Jupyter Lab (no menus yet) and Notebook \n(menus still work).
            • \n
            • Updated requirements to include jupyterlab and labquest packages.
            • \n
          • \n
          • 0.7.9 (Mar. 9 2023)\n
              \n
            • Added spidev package to requirements because pi-plates requires it.
            • \n
            • More robust exception handling when searching for boards/A-to-Ds.
            • \n
          • \n
          • 0.7.8\n
              \n
            • Updated text for insertion into cells to make better use of escaping \nupdates in JPSLUtils >=0.7.0.
            • \n
            • Removed some unnecessary print statements.
            • \n
          • \n
          • 0.7.7\n
              \n
            • Updated requirements for upstream security fixes.
            • \n
            • Conversion to pandas dataframe now works when trace 0 is not collected.
            • \n
            • DAQ menu no longer created in trusted notebooks if the data acquisition \ntools have not been initialized since the notebook was opened.
            • \n
            • Reworked the data collection so that opening an old notebook without \nrunning anything will not have any leftover inoperable or undefined \nwidgets.
            • \n
            • Reordered the live trace display to match the order of the names at right.
            • \n
            • Runs now saved to a human readable html file that includes the run \nconditions.
            • \n
            • As long as this html file is in the same directory as the notebook, the \nrun display can be recreated by running its cell after accidentally \nclearing the cell output.
            • \n
            • The cell displaying the results of a run is now protected against \ndeletion and editing.
            • \n
          • \n
          • 0.7.6\n
              \n
            • Converted to fancy menus (could make hierarchical).
            • \n
          • \n
          • 0.7.5\n
              \n
            • Added fitting to DAQ command menu.
            • \n
            • Documentation Enhancements: github.io website; first pass as API docs; \nreorganized documentation; MyBinder link now forces launch in classic \nnotebook; added plans for adapter board to connect Vernier Sensors.
            • \n
          • \n
          • 0.7.4.1\n
              \n
            • Improved layout of data collection.
            • \n
            • Better widget cleanup.
            • \n
            • Readme fixes.
            • \n
          • \n
          • 0.7.3 Pip install reliability fixes.
          • \n
          • 0.7.2 Suppress Javascript error when not in JLab.
          • \n
          • 0.7.1\n
              \n
            • Include Heat Capacity Lab example.
            • \n
            • Make menu show up in JLab (still not functional).
            • \n
            • Remove matplotlib baggage.
            • \n
          • \n
          • 0.7.0\n
              \n
            • Switched to plotly widget for plotting.
            • \n
            • Added Vernier pressure sensor calibrations (old and new).
            • \n
            • Jupyter widgets based new calculated column GUI.
            • \n
            • Jupyter widgets based new plot GUI.
            • \n
            • Default to providing only one time for channels collected nearly \nsimultaneously.
            • \n
            • As reported values are averages, switched to reporting the estimated \nstandard deviation of the average rather than the deviation of all the \nreadings used to create the average.
            • \n
          • \n
          • 0.6.0 \n
              \n
            • Initial release.
            • \n
            • Live data collection.
            • \n
            • Recognized sensors: ADS1115 boards (voltage, built-in thermistor, \nVernier SS temperature probe), DAQC2 boards (voltage,Vernier SS \ntemperature probe, Vernier standard pH probe, Vernier flat pH probe).
            • \n
          • \n
          \n\n

          Three Channel Adapter Board for Vernier Sensors

          \n\n

          Board plans

          \n\n

          \"board

          \n\n

          Etching the board

          \n\n

          Clean Cu cladding to be etched by wet sanding with 1500 grit or finer.\nDraw traces with a black sharpie \u201cindustrial super permanent ink\u201d version\nworks best. Allow to dry completely (no odor). Can be speeded up using a heat\ngun.

          \n\n

          Etchant recipe:

          \n\n

          3 M HCl

          \n\n

          CuCl2 to make medium emerald green solution (can also be made by \ndissolving Cu after adding hydrogen peroxide)

          \n\n

          Using test piece add 30% H2O2 dropwise to get gentle \nbubbling and \ncomplete\nremoval in 3 \u2013 5 min (longer gives poorly defined edges).

          \n"}, {"fullname": "jupyterpidaq.Boards", "modulename": "jupyterpidaq.Boards", "kind": "module", "doc": "

          This module wraps all the submodules related to communication and control of\ndata acquisition boards.

          \n"}, {"fullname": "jupyterpidaq.Boards.PiGPIO", "modulename": "jupyterpidaq.Boards.PiGPIO", "kind": "module", "doc": "

          This module contains the modules for all PiGPIO based boards.

          \n"}, {"fullname": "jupyterpidaq.Boards.PiGPIO.ADS1115", "modulename": "jupyterpidaq.Boards.PiGPIO.ADS1115", "kind": "module", "doc": "

          \n"}, {"fullname": "jupyterpidaq.Boards.PiGPIO.ADS1115.find_boards", "modulename": "jupyterpidaq.Boards.PiGPIO.ADS1115", "qualname": "find_boards", "kind": "function", "doc": "

          A routine like this must be implemented by all board packages.

          \n\n
          Returns
          \n\n
          \n

          list of ADS1115 board objects (maximum of 4 boards)

          \n
          \n", "signature": "():", "funcdef": "def"}, {"fullname": "jupyterpidaq.Boards.PiGPIO.ADS1115.Board_ADS1115", "modulename": "jupyterpidaq.Boards.PiGPIO.ADS1115", "qualname": "Board_ADS1115", "kind": "class", "doc": "

          Class defining the properties of Adafruit compatible ADS1115\nanalog-to-digital boards for Raspberry-Pi style GPIO. Key characteristics:

          \n\n
            \n
          • 4 channels (0 - 3) with 16 bit resolution and a range of +/- 3.3 V
          • \n
          • Programmable gain on each channel of 2/3, 1, 2, 4, 8, 16 making these\ngood for small signals.
          • \n
          • a differential mode is available but not implemented in this class.
          • \n
          \n", "bases": "jupyterpidaq.Boards.boards.Board"}, {"fullname": "jupyterpidaq.Boards.PiGPIO.ADS1115.Board_ADS1115.__init__", "modulename": "jupyterpidaq.Boards.PiGPIO.ADS1115", "qualname": "Board_ADS1115.__init__", "kind": "function", "doc": "

          Should be overridden by each board and define at minimum:\nself.name = 'board name/adc name/type' Short an useful to end user\nself.vendor = 'Vendor/Manufacturer name`\nself.channels = tuple of available channel IDs\nself.gains = list of gains\nself.Vdd = voltage provided by board to sensors

          \n", "signature": "(adc)"}, {"fullname": "jupyterpidaq.Boards.PiGPIO.ADS1115.Board_ADS1115.getsensors", "modulename": "jupyterpidaq.Boards.PiGPIO.ADS1115", "qualname": "Board_ADS1115.getsensors", "kind": "function", "doc": "

          Return a list of valid sensor object names for this board.

          \n\n
          Returns
          \n\n
          \n

          list of classnames

          \n
          \n", "signature": "(self):", "funcdef": "def"}, {"fullname": "jupyterpidaq.Boards.PiGPIO.ADS1115.Board_ADS1115.V_oversampchan", "modulename": "jupyterpidaq.Boards.PiGPIO.ADS1115", "qualname": "Board_ADS1115.V_oversampchan", "kind": "function", "doc": "

          This routine returns the average voltage for the channel\naveraged at (0.0012 + 1/data_rate)^-1 Hz for avg_sec\nnumber of seconds. The 0.0012 is the required loop time\non a RPI 3B+ in python.

          \n\n

          Returns a tuple of the following 5 objects:\n V_avg -- float, the averaged voltage

          \n\n
          V_min -- float, the minimum voltage read during\nthe interval\n\nV_max -- float, the maximum voltage read during the\ninterval\n\ntime_stamp -- float, the time at halfway through the averaging\ninterval in seconds since the beginning of the epoch (OS\ndependent begin time)\n\nself.Vdd -- float, the reference voltage.\n
          \n\n
          Parameters
          \n\n
            \n
          • int chan: the channel number (0, 1, 2, 3)

          • \n
          • gain: 2/3 (+/-6.144V), 1(+/-4.096V), 2(+/-2.048V),\n4(+/-1.024V), 8 (+/-0.512V), 16 (+/-0.256V))

          • \n
          • int data_rate: the ADC sample rate in Hz (8, 16, 32, 64,\n128,250, 475 or 860 Hz). Set to 475 Hz by default.

          • \n
          • float avg_sec: seconds to average for, actual\naveraging interval will be as close as possible for an integer\nnumber of samples

          • \n
          \n\n

          :returns: V_avg, V_min, V_max, time_stamp, self.Vdd

          \n\n
          Returns
          \n\n
          \n

          description

          \n
          \n\n
          Returns
          \n\n
          \n

          \n
          \n\n
          Returns
          \n\n
          \n

          \n
          \n\n
          Returns
          \n\n
          \n

          \n
          \n\n
          Returns
          \n", "signature": "(self, chan, gain, avg_sec, data_rate=475):", "funcdef": "def"}, {"fullname": "jupyterpidaq.Boards.PiGPIO.ADS1115.Board_ADS1115.V_oversampchan_stats", "modulename": "jupyterpidaq.Boards.PiGPIO.ADS1115", "qualname": "Board_ADS1115.V_oversampchan_stats", "kind": "function", "doc": "

          This routine returns the average voltage for the channel\naveraged at (0.0012 + 1/data_rate)^-1 Hz for avg_sec\nnumber of seconds. The 0.0012 is the required loop time\non a RPI 3B+ in python3. The standard\ndeviation and the estimated deviation of the mean are also\nreturned.

          \n\n

          Returns a tuple of the following 5 objects:\n V_avg -- float, the averaged voltage

          \n\n
          stdev -- float, the standard deviation of the measured values\nduring the averaging interval\n\nstdev_avg -- float, the estimated standard deviation of the\nreturned average\n\ntime_stamp -- float, the time at halfway through the averaging\ninterval in seconds since the beginning of the epoch (OS\ndependent begin time)\n\nself.Vdd -- float, the reference voltage.\n
          \n\n
          Parameters
          \n\n
            \n
          • int chan: the channel number (0, 1, 2, 3)

          • \n
          • gain: 2/3 (+/-6.144V), 1(+/-4.096V), 2(+/-2.048V),\n4(+/-1.024V), 8 (+/-0.512V), 16 (+/-0.256V))

          • \n
          • int data_rate: the ADC sample rate in Hz (8, 16, 32, 64,\n128,250, 475 or 860 Hz). Set to 475 Hz by default.

          • \n
          • float avg_sec: seconds to average for, actual\naveraging interval will be as close as possible for an integer\nnumber of samples

          • \n
          \n\n

          :returns: VV_avg, stdev, stdev_avg, time_stamp, self.Vdd

          \n\n
          Returns
          \n\n
          \n

          description

          \n
          \n\n
          Returns
          \n\n
          \n

          \n
          \n\n
          Returns
          \n\n
          \n

          \n
          \n\n
          Returns
          \n\n
          \n

          \n
          \n\n
          Returns
          \n", "signature": "(self, chan, gain, avg_sec, data_rate=475):", "funcdef": "def"}, {"fullname": "jupyterpidaq.Boards.PiGPIO.ADS1115.Board_ADS1115.V_sampchan", "modulename": "jupyterpidaq.Boards.PiGPIO.ADS1115", "qualname": "Board_ADS1115.V_sampchan", "kind": "function", "doc": "

          This routine returns the voltage for the

          \n\n

          Returns a tuple of the following 3 objects:\n V -- float, the measured voltage

          \n\n
          time_stamp -- float, the time at halfway through the averaging\ninterval in seconds since the beginning of the epoch (OS\ndependent begin time)\n\nself.Vdd -- float, the reference voltage.\n
          \n\n
          Parameters
          \n\n
            \n
          • int chan: the channel number (0, 1, 2, 3)

          • \n
          • gain: 2/3 (+/-6.144V), 1(+/-4.096V), 2(+/-2.048V),\n4(+/-1.024V), 8 (+/-0.512V), 16 (+/-0.256V))

          • \n
          • int data_rate: the ADC sample rate in Hz (8, 16, 32, 64,\n128,250, 475 or 860 Hz). Set to 475 Hz by default.

          • \n
          \n\n

          :returns: V, time_stamp, self.Vdd

          \n\n
          Returns
          \n\n
          \n

          \n
          \n\n
          Returns
          \n\n
          \n

          \n
          \n\n
          Returns
          \n", "signature": "(self, chan, gain, data_rate=475):", "funcdef": "def"}, {"fullname": "jupyterpidaq.Boards.PiGPIO.DAQC2", "modulename": "jupyterpidaq.Boards.PiGPIO.DAQC2", "kind": "module", "doc": "

          \n"}, {"fullname": "jupyterpidaq.Boards.PiGPIO.DAQC2.find_boards", "modulename": "jupyterpidaq.Boards.PiGPIO.DAQC2", "qualname": "find_boards", "kind": "function", "doc": "

          A rountine like this must be implemented by all board packages.

          \n\n
          Returns
          \n\n
          \n

          list of DAQC2 board objects (maximum of 8 boards)

          \n
          \n", "signature": "():", "funcdef": "def"}, {"fullname": "jupyterpidaq.Boards.PiGPIO.DAQC2.Board_DAQC2", "modulename": "jupyterpidaq.Boards.PiGPIO.DAQC2", "qualname": "Board_DAQC2", "kind": "class", "doc": "

          Class defining the properties of the analog-to-digital block of the\npi-Plates DAQC2 board. Key characteristics:

          \n\n
            \n
          • 8 channels (0 - 7) with pseudo 16 bit resolution (oversampled 14 bit) and\na range of +/- 12 V.
          • \n
          • 1 channel (8) dedicated to monitoring Vdd.
          • \n
          • Programmable RGB LED to use as indicator.
          • \n
          • Other available facilities are Digital I/O, Digital-to-Analog and\n2-channel o-scope modes. These are not supported by this class.
          • \n
          \n", "bases": "jupyterpidaq.Boards.boards.Board"}, {"fullname": "jupyterpidaq.Boards.PiGPIO.DAQC2.Board_DAQC2.__init__", "modulename": "jupyterpidaq.Boards.PiGPIO.DAQC2", "qualname": "Board_DAQC2.__init__", "kind": "function", "doc": "

          Should be overridden by each board and define at minimum:\nself.name = 'board name/adc name/type' Short an useful to end user\nself.vendor = 'Vendor/Manufacturer name`\nself.channels = tuple of available channel IDs\nself.gains = list of gains\nself.Vdd = voltage provided by board to sensors

          \n", "signature": "(addr)"}, {"fullname": "jupyterpidaq.Boards.PiGPIO.DAQC2.Board_DAQC2.getsensors", "modulename": "jupyterpidaq.Boards.PiGPIO.DAQC2", "qualname": "Board_DAQC2.getsensors", "kind": "function", "doc": "

          Return a list of valid sensor object names for this board.

          \n\n
          Returns
          \n\n
          \n

          list of classnames

          \n
          \n", "signature": "(self):", "funcdef": "def"}, {"fullname": "jupyterpidaq.Boards.PiGPIO.DAQC2.Board_DAQC2.V_oversampchan", "modulename": "jupyterpidaq.Boards.PiGPIO.DAQC2", "qualname": "Board_DAQC2.V_oversampchan", "kind": "function", "doc": "

          This routine returns the average voltage for the channel\naveraged at the default rate for the board and returns an\naverage and observed range.

          \n\n

          Returns a tuple of the following 5 objects:\n V_avg -- float, the averaged voltage

          \n\n
          V_min -- float, the minimum voltage read during\nthe interval\n\nV_max -- float, the maximum voltage read during the\ninterval\n\ntime_stamp -- float, the time at halfway through the averaging\ninterval in seconds since the beginning of the epoch (OS\ndependent begin time)\n\nVdd_avg -- float, the reference voltage (Vdd) collected\nsimultaneously.\n
          \n\n
          Parameters
          \n\n
            \n
          • int chan: the channel number (0, 1, 2, 3, 4, 5, 6, 7,\n8). NOTE: channel 8 returns a measurement of Vdd.

          • \n
          • gain: ignored by board. Defaults to 1.

          • \n
          • int data_rate: ignored by board.

          • \n
          • float avg_sec: seconds to average for, actual\naveraging interval will be as close as possible for an integer\nnumber of samples

          • \n
          \n\n

          :returns: V_avg, V_min, V_max, time_stamp, Vdd_avg

          \n\n
          Returns
          \n\n
          \n

          description

          \n
          \n\n
          Returns
          \n\n
          \n

          \n
          \n\n
          Returns
          \n\n
          \n

          \n
          \n\n
          Returns
          \n\n
          \n

          \n
          \n\n
          Returns
          \n", "signature": "(self, chan, gain, avg_sec, data_rate=475):", "funcdef": "def"}, {"fullname": "jupyterpidaq.Boards.PiGPIO.DAQC2.Board_DAQC2.V_oversampchan_stats", "modulename": "jupyterpidaq.Boards.PiGPIO.DAQC2", "qualname": "Board_DAQC2.V_oversampchan_stats", "kind": "function", "doc": "

          This routine returns the average voltage for the channel\naveraged at the maximum rate for the board. The standard\ndeviation and the estimated deviation of the mean are also\nreturned.

          \n\n

          Returns a tuple of the following 5 objects:\n V_avg -- float, the averaged voltage

          \n\n
          stdev -- float, the standard deviation of the measured values\nduring the averaging interval\n\nstdev_avg -- float, the estimated standard deviation of the\nreturned average\n\ntime_stamp -- float, the time at halfway through the averaging\ninterval in seconds since the beginning of the epoch (OS\ndependent begin time)\n\nVdd_avg -- float, the reference voltage (Vdd) collected\nsimultaneously.\n
          \n\n
          Parameters
          \n\n
            \n
          • int chan: the channel number (0, 1, 2, 3, 4, 5, 6, 7,\n8). NOTE: channel 8 returns a measurement of Vdd.

          • \n
          • gain: ignored by board. Defaults to 1.

          • \n
          • int data_rate: ignored by board.

          • \n
          • float avg_sec: seconds to average for, actual\naveraging interval will be as close as possible for an integer\nnumber of samples

          • \n
          \n\n

          :returns: V_avg, stdev, stdev_avg, time_stamp, Vdd_avg

          \n\n
          Returns
          \n\n
          \n

          description

          \n
          \n\n
          Returns
          \n\n
          \n

          \n
          \n\n
          Returns
          \n\n
          \n

          \n
          \n\n
          Returns
          \n\n
          \n

          \n
          \n\n
          Returns
          \n", "signature": "(self, chan, gain, avg_sec, data_rate=475):", "funcdef": "def"}, {"fullname": "jupyterpidaq.Boards.PiGPIO.DAQC2.Board_DAQC2.V_sampchan", "modulename": "jupyterpidaq.Boards.PiGPIO.DAQC2", "qualname": "Board_DAQC2.V_sampchan", "kind": "function", "doc": "

          This routine returns a single reading of the voltage for the channel.

          \n\n

          Returns a tuple of the following 5 objects:\n V -- float, the measured voltage

          \n\n
          time_stamp -- float, the time of the measurement in seconds since\nthe beginning of the epoch (OS dependent begin time)\n\nref -- float, the reference voltage (Vdd) collected\nsimultaneously.\n
          \n\n
          Parameters
          \n\n
            \n
          • int chan: the channel number (0, 1, 2, 3, 4, 5, 6, 7,\n8). NOTE: channel 8 returns a measurement of Vdd.

          • \n
          • gain: ignored by board. Defaults to 1.

          • \n
          • int data_rate: ignored by board.

          • \n
          \n\n

          :returns: V_avg, stdev, stdev_avg, time_stamp, Vdd_avg

          \n\n
          Returns
          \n\n
          \n

          \n
          \n\n
          Returns
          \n\n
          \n

          \n
          \n\n
          Returns
          \n", "signature": "(self, chan, gain, data_rate=475):", "funcdef": "def"}, {"fullname": "jupyterpidaq.Boards.Simulated", "modulename": "jupyterpidaq.Boards.Simulated", "kind": "module", "doc": "

          This module wraps modules for simulated boards.

          \n"}, {"fullname": "jupyterpidaq.Boards.Simulated.ADCsim", "modulename": "jupyterpidaq.Boards.Simulated.ADCsim", "kind": "module", "doc": "

          \n"}, {"fullname": "jupyterpidaq.Boards.Simulated.ADCsim.find_boards", "modulename": "jupyterpidaq.Boards.Simulated.ADCsim", "qualname": "find_boards", "kind": "function", "doc": "

          \n", "signature": "():", "funcdef": "def"}, {"fullname": "jupyterpidaq.Boards.Simulated.ADCsim.Board_ADCsim_random", "modulename": "jupyterpidaq.Boards.Simulated.ADCsim", "qualname": "Board_ADCsim_random", "kind": "class", "doc": "

          Base class for all boards. Each board should be an extension of this class.

          \n", "bases": "jupyterpidaq.Boards.boards.Board"}, {"fullname": "jupyterpidaq.Boards.Simulated.ADCsim.Board_ADCsim_random.__init__", "modulename": "jupyterpidaq.Boards.Simulated.ADCsim", "qualname": "Board_ADCsim_random.__init__", "kind": "function", "doc": "

          Should be overridden by each board and define at minimum:\nself.name = 'board name/adc name/type' Short an useful to end user\nself.vendor = 'Vendor/Manufacturer name`\nself.channels = tuple of available channel IDs\nself.gains = list of gains\nself.Vdd = voltage provided by board to sensors

          \n", "signature": "(adc)"}, {"fullname": "jupyterpidaq.Boards.Simulated.ADCsim.Board_ADCsim_random.getsensors", "modulename": "jupyterpidaq.Boards.Simulated.ADCsim", "qualname": "Board_ADCsim_random.getsensors", "kind": "function", "doc": "

          Return a list of valid sensor object names for this board.

          \n\n
          Returns
          \n\n
          \n

          list of classnames

          \n
          \n", "signature": "(self):", "funcdef": "def"}, {"fullname": "jupyterpidaq.Boards.Simulated.ADCsim.Board_ADCsim_random.V_oversampchan_stats", "modulename": "jupyterpidaq.Boards.Simulated.ADCsim", "qualname": "Board_ADCsim_random.V_oversampchan_stats", "kind": "function", "doc": "

          This routine returns the average voltage for the channel\naveraged at (0.0012 + 1/data_rate)^-1 Hz for avg_sec\nnumber of seconds. The 0.0012 is the required loop time\non a RPI 3B+ in python3. The voltage is rounded to the number\nof decimals indicated by the standard deviation. The standard\ndeviation and the estimated deviation of the mean are also\nreturned.\nParameters\n chan the channel number 0, 1, 2, 3\n gain 2/3 (+/-6.144V), 1(+/-4.096V), 2(+/-2.048V), 4(+/-1.024V),\n 8 (+/-0.512V), 16 (+/-0.256V)\n data_rate the ADC sample rate in Hz (8, 16, 32, 64, 128, 250, 475 or 860 Hz)\n avg_sec seconds to average for, actual averaging interval will be as close\n as possible for an integer number of samples.\nReturns a tuple (V_avg, V_min, V_max, time_stamp)\n V_avg the averaged voltage\n stdev estimated standard deviation of the measurements\n stdev_avg estimated standard deviation of the mean\n time_stamp the time at halfway through the averaging interval in seconds\n since the beginning of the epoch (OS dependent begin time).

          \n", "signature": "(self, chan, gain, avg_sec, data_rate=475):", "funcdef": "def"}, {"fullname": "jupyterpidaq.Boards.Simulated.ADCsim.Board_ADCsim_random.V_oversampchan", "modulename": "jupyterpidaq.Boards.Simulated.ADCsim", "qualname": "Board_ADCsim_random.V_oversampchan", "kind": "function", "doc": "

          This routine returns the average voltage for the channel\naveraged at (0.0012 + 1/data_rate)^-1 Hz for avg_sec\nnumber of seconds. The 0.0012 is the required loop time\non a RPI 3B+ in python3. The voltage is rounded to the number\nof decimals indicated by the standard deviation. The standard\ndeviation and the estimated deviation of the mean are also\nreturned.\nParameters\n chan the channel number 0, 1, 2, 3\n gain 2/3 (+/-6.144V), 1(+/-4.096V), 2(+/-2.048V), 4(+/-1.024V),\n 8 (+/-0.512V), 16 (+/-0.256V)\n data_rate the ADC sample rate in Hz (8, 16, 32, 64, 128, 250, 475 or 860 Hz)\n avg_sec seconds to average for, actual averaging interval will be as close\n as possible for an integer number of samples.\nReturns a tuple (V_avg, V_min, V_max, time_stamp)\n V_avg the averaged voltage\n stdev estimated standard deviation of the measurements\n stdev_avg estimated standard deviation of the mean\n time_stamp the time at halfway through the averaging interval in seconds\n since the beginning of the epoch (OS dependent begin time).

          \n", "signature": "(self, chan, gain, avg_sec, data_rate=475):", "funcdef": "def"}, {"fullname": "jupyterpidaq.Boards.Simulated.ADCsim.Board_ADCsim_random.V_sampchan", "modulename": "jupyterpidaq.Boards.Simulated.ADCsim", "qualname": "Board_ADCsim_random.V_sampchan", "kind": "function", "doc": "

          This function returns a single measurement and the time it was\ncollected.

          \n\n
          Parameters
          \n\n
            \n
          • chan: id of the channel to be measured
          • \n
          • gain: gain of the channel if adjustable
          • \n
          \n\n
          Returns
          \n\n
          \n

          a tuple consisting of V, time_stamp, where V = the single\n voltage measurement and time_stamp the time it was collected.

          \n
          \n", "signature": "(self, chan, gain, **kwargs):", "funcdef": "def"}, {"fullname": "jupyterpidaq.Boards.Simulated.ADCsim_line", "modulename": "jupyterpidaq.Boards.Simulated.ADCsim_line", "kind": "module", "doc": "

          \n"}, {"fullname": "jupyterpidaq.Boards.Simulated.ADCsim_line.find_boards", "modulename": "jupyterpidaq.Boards.Simulated.ADCsim_line", "qualname": "find_boards", "kind": "function", "doc": "

          \n", "signature": "():", "funcdef": "def"}, {"fullname": "jupyterpidaq.Boards.Simulated.ADCsim_line.Board_ADCsim_line", "modulename": "jupyterpidaq.Boards.Simulated.ADCsim_line", "qualname": "Board_ADCsim_line", "kind": "class", "doc": "

          This class simulates an Analog-to-Digital board that returns a linearly\nincreasing signal with a small amount of noise on the signal. The\nintercept and slope depend upon which hour of the day the simulation is\nrun.

          \n", "bases": "jupyterpidaq.Boards.boards.Board"}, {"fullname": "jupyterpidaq.Boards.Simulated.ADCsim_line.Board_ADCsim_line.__init__", "modulename": "jupyterpidaq.Boards.Simulated.ADCsim_line", "qualname": "Board_ADCsim_line.__init__", "kind": "function", "doc": "

          Should be overridden by each board and define at minimum:\nself.name = 'board name/adc name/type' Short an useful to end user\nself.vendor = 'Vendor/Manufacturer name`\nself.channels = tuple of available channel IDs\nself.gains = list of gains\nself.Vdd = voltage provided by board to sensors

          \n", "signature": "(adc)"}, {"fullname": "jupyterpidaq.Boards.Simulated.ADCsim_line.Board_ADCsim_line.getsensors", "modulename": "jupyterpidaq.Boards.Simulated.ADCsim_line", "qualname": "Board_ADCsim_line.getsensors", "kind": "function", "doc": "

          Return a list of valid sensor object names for this board.

          \n\n
          Returns
          \n\n
          \n

          list of classnames

          \n
          \n", "signature": "(self):", "funcdef": "def"}, {"fullname": "jupyterpidaq.Boards.Simulated.ADCsim_line.Board_ADCsim_line.V_oversampchan", "modulename": "jupyterpidaq.Boards.Simulated.ADCsim_line", "qualname": "Board_ADCsim_line.V_oversampchan", "kind": "function", "doc": "

          This routine returns the average voltage for the channel\naveraged at (0.0012 + 1/data_rate)^-1 Hz for avg_sec\nnumber of seconds. The 0.0012 is the required loop time\non a RPI 3B+ in python3. The voltage is rounded to the number\nof decimals indicated by the standard deviation. The standard\ndeviation and the estimated deviation of the mean are also\nreturned.\nParameters\n chan the channel number 0, 1, 2, 3\n gain 2/3 (+/-6.144V), 1(+/-4.096V), 2(+/-2.048V), 4(+/-1.024V),\n 8 (+/-0.512V), 16 (+/-0.256V)\n data_rate the ADC sample rate in Hz (8, 16, 32, 64, 128, 250, 475\n or 860 Hz)\n avg_sec seconds to average for, actual averaging interval will be\n as close as possible for an integer number of samples.\nReturns a tuple (V_avg, V_min, V_max, time_stamp)\n V_avg the averaged voltage\n stdev estimated standard deviation of the measurements\n stdev_avg estimated standard deviation of the mean\n time_stamp the time at halfway through the averaging interval in\n seconds since the beginning of the epoch (OS dependent begin\n time).

          \n", "signature": "(self, chan, gain, avg_sec, data_rate=475):", "funcdef": "def"}, {"fullname": "jupyterpidaq.Boards.Simulated.ADCsim_line.Board_ADCsim_line.V_oversampchan_stats", "modulename": "jupyterpidaq.Boards.Simulated.ADCsim_line", "qualname": "Board_ADCsim_line.V_oversampchan_stats", "kind": "function", "doc": "

          This routine returns the average voltage for the channel\naveraged at (0.0012 + 1/data_rate)^-1 Hz for avg_sec\nnumber of seconds. The 0.0012 is the required loop time\non a RPI 3B+ in python3. The voltage is rounded to the number\nof decimals indicated by the standard deviation. The standard\ndeviation and the estimated deviation of the mean are also\nreturned.\nParameters\n chan the channel number 0, 1, 2, 3\n gain 2/3 (+/-6.144V), 1(+/-4.096V), 2(+/-2.048V), 4(+/-1.024V),\n 8 (+/-0.512V), 16 (+/-0.256V)\n data_rate the ADC sample rate in Hz (8, 16, 32, 64, 128, 250, 475\n or 860 Hz)\n avg_sec seconds to average for, actual averaging interval will be\n as close as possible for an integer number of samples.\nReturns a tuple (V_avg, V_min, V_max, time_stamp)\n V_avg the averaged voltage\n stdev estimated standard deviation of the measurements\n stdev_avg estimated standard deviation of the mean\n time_stamp the time at halfway through the averaging interval in\n seconds since the beginning of the epoch (OS dependent begin\n time).

          \n", "signature": "(self, chan, gain, avg_sec, data_rate=475):", "funcdef": "def"}, {"fullname": "jupyterpidaq.Boards.Simulated.ADCsim_line.Board_ADCsim_line.V_sampchan", "modulename": "jupyterpidaq.Boards.Simulated.ADCsim_line", "qualname": "Board_ADCsim_line.V_sampchan", "kind": "function", "doc": "

          This routine returns the average voltage for the channel\naveraged at (0.0012 + 1/data_rate)^-1 Hz for avg_sec\nnumber of seconds. The 0.0012 is the required loop time\non a RPI 3B+ in python3. The voltage is rounded to the number\nof decimals indicated by the standard deviation. The standard\ndeviation and the estimated deviation of the mean are also\nreturned.\nParameters\n chan the channel number 0, 1, 2, 3\n gain 2/3 (+/-6.144V), 1(+/-4.096V), 2(+/-2.048V), 4(+/-1.024V),\n 8 (+/-0.512V), 16 (+/-0.256V)\n data_rate the ADC sample rate in Hz (8, 16, 32, 64, 128, 250, 475\n or 860 Hz) avg_sec seconds to average for, actual averaging\n interval will be as close as possible for an integer number of\n samples.\nReturns a tuple (V_avg, V_min, V_max, time_stamp)\n V_avg the averaged voltage\n stdev estimated standard deviation of the measurements\n stdev_avg estimated standard deviation of the mean\n time_stamp the time at halfway through the averaging interval in\n seconds since the beginning of the epoch (OS dependent begin\n time).

          \n", "signature": "(self, chan, gain, data_rate=475):", "funcdef": "def"}, {"fullname": "jupyterpidaq.Boards.boards", "modulename": "jupyterpidaq.Boards.boards", "kind": "module", "doc": "

          This file handles loading adc board control software and sensor information.\nIt uses the list of known boards. It will skip boards that produce an error\neither because the pypi package is not installed or an error occurs when\ntrying to communicate with the board.

          \n\n

          The ADC simulator will be installed if no boards are available.

          \n"}, {"fullname": "jupyterpidaq.Boards.boards.load_boards", "modulename": "jupyterpidaq.Boards.boards", "qualname": "load_boards", "kind": "function", "doc": "

          Uses the list of known board packages to search for available boards.\nThe file .py should at minimum\nimplement a find_boards(): routine that overrides the function below and\ndefine a class for the particular board that extends theBoard` class\ndefined below.

          \n\n
          Returns
          \n\n
          \n

          list of adc board objects.

          \n
          \n", "signature": "():", "funcdef": "def"}, {"fullname": "jupyterpidaq.Boards.boards.find_boards", "modulename": "jupyterpidaq.Boards.boards", "qualname": "find_boards", "kind": "function", "doc": "

          A function overriding this must be implemented by all board packages.\nSee examples in working packages. This is highly board dependent.

          \n\n
          Returns
          \n\n
          \n

          list of board objects

          \n
          \n", "signature": "():", "funcdef": "def"}, {"fullname": "jupyterpidaq.Boards.boards.Board", "modulename": "jupyterpidaq.Boards.boards", "qualname": "Board", "kind": "class", "doc": "

          Base class for all boards. Each board should be an extension of this class.

          \n"}, {"fullname": "jupyterpidaq.Boards.boards.Board.__init__", "modulename": "jupyterpidaq.Boards.boards", "qualname": "Board.__init__", "kind": "function", "doc": "

          Should be overridden by each board and define at minimum:\nself.name = 'board name/adc name/type' Short an useful to end user\nself.vendor = 'Vendor/Manufacturer name`\nself.channels = tuple of available channel IDs\nself.gains = list of gains\nself.Vdd = voltage provided by board to sensors

          \n", "signature": "()"}, {"fullname": "jupyterpidaq.Boards.boards.Board.getname", "modulename": "jupyterpidaq.Boards.boards", "qualname": "Board.getname", "kind": "function", "doc": "
          Returns
          \n\n
          \n

          string value of the board name, a short label of board type.

          \n
          \n", "signature": "(self):", "funcdef": "def"}, {"fullname": "jupyterpidaq.Boards.boards.Board.getchannels", "modulename": "jupyterpidaq.Boards.boards", "qualname": "Board.getchannels", "kind": "function", "doc": "
          Returns
          \n\n
          \n

          tuple of ids for available channels

          \n
          \n", "signature": "(self):", "funcdef": "def"}, {"fullname": "jupyterpidaq.Boards.boards.Board.getgains", "modulename": "jupyterpidaq.Boards.boards", "qualname": "Board.getgains", "kind": "function", "doc": "

          If not defined for a specific board the gain is fixed at 1.

          \n\n
          Returns
          \n\n
          \n

          tuple of gains availabe for onboard preamp

          \n
          \n", "signature": "(self):", "funcdef": "def"}, {"fullname": "jupyterpidaq.Boards.boards.Board.getvendor", "modulename": "jupyterpidaq.Boards.boards", "qualname": "Board.getvendor", "kind": "function", "doc": "
          Returns
          \n\n
          \n

          string value of the vendor name

          \n
          \n", "signature": "(self):", "funcdef": "def"}, {"fullname": "jupyterpidaq.Boards.boards.Board.getVdd", "modulename": "jupyterpidaq.Boards.boards", "qualname": "Board.getVdd", "kind": "function", "doc": "
          Returns
          \n\n
          \n

          numerical value of the Vdd, voltage provided to sensors

          \n
          \n", "signature": "(self):", "funcdef": "def"}, {"fullname": "jupyterpidaq.Boards.boards.Board.getsensors", "modulename": "jupyterpidaq.Boards.boards", "qualname": "Board.getsensors", "kind": "function", "doc": "

          This returns a list of objects that allow the software to translate\nthe measured voltage into a sensor reading in appropriate units.\nMust be provided by the specific board implementation. See examples\nin working board packages.

          \n\n
          Returns
          \n\n
          \n

          A list of valid sensor objects to use with this board. This\n should be a subset of all the sensors returned by the listSensors\n function in sensors.py.

          \n
          \n", "signature": "(self):", "funcdef": "def"}, {"fullname": "jupyterpidaq.Boards.boards.Board.V_oversampchan", "modulename": "jupyterpidaq.Boards.boards", "qualname": "Board.V_oversampchan", "kind": "function", "doc": "

          This function should return a tuple with average, minimum and maximum\nfor a channel averaged over the period of time avg_sec. How the\naveraging is performed will depend on the board.

          \n\n
          Parameters
          \n\n
            \n
          • chan: id of the channel to be measured
          • \n
          • gain: gain of the channel if adjustable
          • \n
          • avg_sec: float period of time over which to average
          • \n
          \n\n
          Returns
          \n\n
          \n

          a tuple consisting of V_avg, V_min, V_max, time_stamp, avg_Vdd\n The time_stamp is the time the data was collected, usually the\n middle of the averaging period. avg_Vdd should be the measured\n average Vdd taken simultaneously, immediately before,\n or immediately after the voltage being measured. If the board or\n power supply is very stable self.Vdd can be returned instead.

          \n
          \n", "signature": "(self, chan, gain, avg_sec, **kwargs):", "funcdef": "def"}, {"fullname": "jupyterpidaq.Boards.boards.Board.V_oversampchan_stats", "modulename": "jupyterpidaq.Boards.boards", "qualname": "Board.V_oversampchan_stats", "kind": "function", "doc": "

          This function should return a tuple of statistical information for a\nchannel averaged over the period of time avg_sec.

          \n\n
          Parameters
          \n\n
            \n
          • chan: id of the channel to be measured
          • \n
          • gain: gain of the channel if adjustable
          • \n
          • avg_sec: float period of time over which to average
          • \n
          \n\n
          Returns
          \n\n
          \n

          tuple consisting of V_avg, stdev, stdev_avg, time_stamp,\n avg_Vdd where stdev_avg is the estimated standard deviation\n of the average not the standard deviation of the values\n sampled (stdev). avg_Vdd should be the measured\n average Vdd taken simultaneously, immediately before,\n or immediately after the voltage being measured. If the board or\n power supply is very stable self.Vdd can be returned instead.

          \n
          \n", "signature": "(self, chan, gain, avg_sec, **kwargs):", "funcdef": "def"}, {"fullname": "jupyterpidaq.Boards.boards.Board.V_sampchan", "modulename": "jupyterpidaq.Boards.boards", "qualname": "Board.V_sampchan", "kind": "function", "doc": "

          This function returns a single measurement and the time it was\ncollected.

          \n\n
          Parameters
          \n\n
            \n
          • chan: id of the channel to be measured
          • \n
          • gain: gain of the channel if adjustable
          • \n
          \n\n
          Returns
          \n\n
          \n

          a tuple consisting of V, time_stamp, ref_Vdd, where V = the\n single voltage measurement and time_stamp the time it was\n collected. ref_Vdd should be the measured\n Vdd taken simultaneously, immediately before,\n or immediately after the voltage being measured. If the board or\n power supply is very stable self.Vdd can be returned instead.

          \n
          \n", "signature": "(self, chan, gain, **kwargs):", "funcdef": "def"}, {"fullname": "jupyterpidaq.Boards.vernier", "modulename": "jupyterpidaq.Boards.vernier", "kind": "module", "doc": "

          This module contains modules for the Vernier Interfaces.

          \n"}, {"fullname": "jupyterpidaq.Boards.vernier.labquest", "modulename": "jupyterpidaq.Boards.vernier.labquest", "kind": "module", "doc": "

          \n"}, {"fullname": "jupyterpidaq.Boards.vernier.labquest.find_boards", "modulename": "jupyterpidaq.Boards.vernier.labquest", "qualname": "find_boards", "kind": "function", "doc": "

          A rountine like this must be implemented by all board packages.

          \n\n
          Returns
          \n\n
          \n

          list of LabQuests (Types too?)

          \n
          \n", "signature": "():", "funcdef": "def"}, {"fullname": "jupyterpidaq.Boards.vernier.labquest.Board_LQ", "modulename": "jupyterpidaq.Boards.vernier.labquest", "qualname": "Board_LQ", "kind": "class", "doc": "

          Class defining the properties of the analog-to-digital block of the\nLabQuests. Key characteristics:

          \n\n
            \n
          • 3 channels (1, 2, 3) a range of +/- 10 V.
          • \n
          • 12 bit resolution
          • \n
          • Other available but not implemented facilities are Digital I/O.
          • \n
          \n", "bases": "jupyterpidaq.Boards.boards.Board"}, {"fullname": "jupyterpidaq.Boards.vernier.labquest.Board_LQ.__init__", "modulename": "jupyterpidaq.Boards.vernier.labquest", "qualname": "Board_LQ.__init__", "kind": "function", "doc": "

          Should be overridden by each board and define at minimum:\nself.name = 'board name/adc name/type' Short an useful to end user\nself.vendor = 'Vendor/Manufacturer name`\nself.channels = tuple of available channel IDs\nself.gains = list of gains\nself.Vdd = voltage provided by board to sensors

          \n", "signature": "(addr, send, rcv)"}, {"fullname": "jupyterpidaq.Boards.vernier.labquest.Board_LQ.getsensors", "modulename": "jupyterpidaq.Boards.vernier.labquest", "qualname": "Board_LQ.getsensors", "kind": "function", "doc": "

          Return a list of valid sensor object names for this board.

          \n\n
          Returns
          \n\n
          \n

          list of classnames

          \n
          \n", "signature": "(self):", "funcdef": "def"}, {"fullname": "jupyterpidaq.Boards.vernier.labquest.Board_LQ.V_oversampchan", "modulename": "jupyterpidaq.Boards.vernier.labquest", "qualname": "Board_LQ.V_oversampchan", "kind": "function", "doc": "

          This routine returns the average voltage for the channel\naveraged at the default rate for the board and returns an\naverage and observed range.

          \n\n

          Returns a tuple of the following 5 objects:\n V_avg -- float, the averaged voltage

          \n\n
          V_min -- float, the minimum voltage read during\nthe interval\n\nV_max -- float, the maximum voltage read during the\ninterval\n\ntime_stamp -- float, the time at halfway through the averaging\ninterval in seconds since the beginning of the epoch (OS\ndependent begin time)\n\nVdd_avg -- float, the reference voltage (Vdd) collected\nsimultaneously.\n
          \n\n
          Parameters
          \n\n
            \n
          • int chan: the channel number (1, 2, 3)

          • \n
          • gain: ignored by board. Defaults to 1.

          • \n
          • int data_rate: maximum

          • \n
          • float avg_sec: seconds to average for, actual\naveraging interval will be as close as possible for an integer\nnumber of samples

          • \n
          \n\n

          :returns: V_avg, V_min, V_max, time_stamp, Vdd_avg

          \n\n
          Returns
          \n\n
          \n

          description

          \n
          \n\n
          Returns
          \n\n
          \n

          \n
          \n\n
          Returns
          \n\n
          \n

          \n
          \n\n
          Returns
          \n\n
          \n

          \n
          \n\n
          Returns
          \n", "signature": "(self, chan, gain, avg_sec, data_rate=10000):", "funcdef": "def"}, {"fullname": "jupyterpidaq.Boards.vernier.labquest.Board_LQ.V_oversampchan_stats", "modulename": "jupyterpidaq.Boards.vernier.labquest", "qualname": "Board_LQ.V_oversampchan_stats", "kind": "function", "doc": "

          This routine returns the average voltage for the channel\naveraged at the maximum rate for the board. The standard\ndeviation and the estimated deviation of the mean are also\nreturned.

          \n\n

          Returns a tuple of the following 5 objects:\n V_avg -- float, the averaged voltage

          \n\n
          stdev -- float, the standard deviation of the measured values\nduring the averaging interval\n\nstdev_avg -- float, the estimated standard deviation of the\nreturned average\n\ntime_stamp -- float, the time at halfway through the averaging\ninterval in seconds since the beginning of the epoch (OS\ndependent begin time)\n\nVdd_avg -- float, returned as None.\n
          \n\n
          Parameters
          \n\n
            \n
          • int chan: the channel number (1, 2, 3)

          • \n
          • gain: ignored by board. Defaults to 1.

          • \n
          • int data_rate:

          • \n
          • float avg_sec: seconds to average for, actual\naveraging interval will be as close as possible for an integer\nnumber of samples

          • \n
          \n\n

          :returns: V_avg, stdev, stdev_avg, time_stamp, Vdd_avg

          \n\n
          Returns
          \n\n
          \n

          \n
          \n\n
          Returns
          \n\n
          \n

          \n
          \n\n
          Returns
          \n\n
          \n

          \n
          \n\n
          Returns
          \n\n
          \n

          \n
          \n\n
          Returns
          \n", "signature": "(self, chan, gain, avg_sec, data_rate=10000):", "funcdef": "def"}, {"fullname": "jupyterpidaq.Boards.vernier.labquest.Board_LQ.V_sampchan", "modulename": "jupyterpidaq.Boards.vernier.labquest", "qualname": "Board_LQ.V_sampchan", "kind": "function", "doc": "

          This routine returns a single reading of the voltage for the channel.

          \n\n

          Returns a tuple of the following 5 objects:\n V -- float, the measured voltage

          \n\n
          time_stamp -- float, the time of the measurement in seconds since\nthe beginning of the epoch (OS dependent begin time)\n\nref -- float, the reference voltage (Vdd) collected\nsimultaneously.\n
          \n\n
          Parameters
          \n\n
            \n
          • int chan: the channel number (1, 2, 3).

          • \n
          • gain: ignored by board. Defaults to 1.

          • \n
          • int data_rate:

          • \n
          \n\n

          :returns float value:

          \n\n
          Returns
          \n\n
          \n

          \n
          \n\n
          Returns
          \n", "signature": "(self, chan, gain, data_rate=10000):", "funcdef": "def"}, {"fullname": "jupyterpidaq.Boards.vernier.labquest.LQProc", "modulename": "jupyterpidaq.Boards.vernier.labquest", "qualname": "LQProc", "kind": "function", "doc": "

          Process to spawn that continuously collects from the LabQuests(s)

          \n\n

          Parameters

          \n\n

          cmdrcv: Pipe\n Where commands are received.

          \n\n

          datasend: Pipe\n Where data is sent in response to a command.

          \n", "signature": "(cmdrcv, datasend, starttime, samples):", "funcdef": "def"}, {"fullname": "jupyterpidaq.ChannelSettings", "modulename": "jupyterpidaq.ChannelSettings", "kind": "module", "doc": "

          \n"}, {"fullname": "jupyterpidaq.ChannelSettings.ChannelSettings", "modulename": "jupyterpidaq.ChannelSettings", "qualname": "ChannelSettings", "kind": "class", "doc": "

          This class takes care of interacting with the user to get settings for data\ncollection on each channel. Should be initialized with an idno, so that\nit knows what number has been assigned to it.

          \n"}, {"fullname": "jupyterpidaq.ChannelSettings.ChannelSettings.__init__", "modulename": "jupyterpidaq.ChannelSettings", "qualname": "ChannelSettings.__init__", "kind": "function", "doc": "
          Parameters
          \n\n
            \n
          • idno: int number iding this instance of ChannelSettings
          • \n
          \n", "signature": "(idno, availboards)"}, {"fullname": "jupyterpidaq.ChannelSettings.ChannelSettings.activate", "modulename": "jupyterpidaq.ChannelSettings", "qualname": "ChannelSettings.activate", "kind": "function", "doc": "

          This function makes this channel active. No return value unless an e\nrror is thrown by something called by this function.

          \n\n
          Returns
          \n\n
          \n

          None

          \n
          \n", "signature": "(self):", "funcdef": "def"}, {"fullname": "jupyterpidaq.ChannelSettings.ChannelSettings.deactivate", "modulename": "jupyterpidaq.ChannelSettings", "qualname": "ChannelSettings.deactivate", "kind": "function", "doc": "

          This function makes the channel inactive. No return value unless an\nerror is thrown by something called by this function.

          \n\n
          Returns
          \n\n
          \n

          None

          \n
          \n", "signature": "(self):", "funcdef": "def"}, {"fullname": "jupyterpidaq.ChannelSettings.ChannelSettings.checkchanged", "modulename": "jupyterpidaq.ChannelSettings", "qualname": "ChannelSettings.checkchanged", "kind": "function", "doc": "

          This function is called when the checkbox changes.

          \n\n
          Parameters
          \n\n
            \n
          • self:
          • \n
          • change: change object passed by the observe tool
          • \n
          \n\n
          Returns
          \n\n
          \n

          None

          \n
          \n", "signature": "(self, change):", "funcdef": "def"}, {"fullname": "jupyterpidaq.ChannelSettings.ChannelSettings.boardchanged", "modulename": "jupyterpidaq.ChannelSettings", "qualname": "ChannelSettings.boardchanged", "kind": "function", "doc": "

          This function responds to a change in board choice.

          \n\n
          Parameters
          \n\n
            \n
          • change: change object passed by the observe tool
          • \n
          \n\n
          Returns
          \n", "signature": "(self, change):", "funcdef": "def"}, {"fullname": "jupyterpidaq.ChannelSettings.ChannelSettings.channelchanged", "modulename": "jupyterpidaq.ChannelSettings", "qualname": "ChannelSettings.channelchanged", "kind": "function", "doc": "

          This function responds to a change in board choice.

          \n\n
          Parameters
          \n\n
            \n
          • change: change object passed by the observe tool
          • \n
          \n\n
          Returns
          \n", "signature": "(self, change):", "funcdef": "def"}, {"fullname": "jupyterpidaq.ChannelSettings.ChannelSettings.sensorchanged", "modulename": "jupyterpidaq.ChannelSettings", "qualname": "ChannelSettings.sensorchanged", "kind": "function", "doc": "

          Called by the observe function of sensorchoice when the user changes\nthe sensor choice.

          \n\n
          Parameters
          \n\n
            \n
          • self:
          • \n
          • change: change object passed by the observe tool
          • \n
          \n\n
          Returns
          \n\n
          \n

          None

          \n
          \n", "signature": "(self, change):", "funcdef": "def"}, {"fullname": "jupyterpidaq.ChannelSettings.ChannelSettings.unitschanged", "modulename": "jupyterpidaq.ChannelSettings", "qualname": "ChannelSettings.unitschanged", "kind": "function", "doc": "

          Called by the observe function for the units selector when units are\nchanged

          \n\n
          Parameters
          \n\n
            \n
          • self:
          • \n
          • change: change object passed by the observe tool
          • \n
          \n\n
          Returns
          \n", "signature": "(self, change):", "funcdef": "def"}, {"fullname": "jupyterpidaq.ChannelSettings.ChannelSettings.gainschanged", "modulename": "jupyterpidaq.ChannelSettings", "qualname": "ChannelSettings.gainschanged", "kind": "function", "doc": "

          Called by the observe function for the gains selector when the gain is\nchanged.

          \n\n
          Parameters
          \n\n
            \n
          • self:
          • \n
          • change: change object passed by the observe tool
          • \n
          \n\n
          Returns
          \n", "signature": "(self, change):", "funcdef": "def"}, {"fullname": "jupyterpidaq.ChannelSettings.ChannelSettings.setup", "modulename": "jupyterpidaq.ChannelSettings", "qualname": "ChannelSettings.setup", "kind": "function", "doc": "

          Sets up the GUI and the necessary monitoring.

          \n\n
          Returns
          \n\n
          \n

          None

          \n
          \n", "signature": "(self):", "funcdef": "def"}, {"fullname": "jupyterpidaq.ChannelSettings.ChannelSettings.hideGUI", "modulename": "jupyterpidaq.ChannelSettings", "qualname": "ChannelSettings.hideGUI", "kind": "function", "doc": "

          \n", "signature": "(self):", "funcdef": "def"}, {"fullname": "jupyterpidaq.DAQProc", "modulename": "jupyterpidaq.DAQProc", "kind": "module", "doc": "

          \n"}, {"fullname": "jupyterpidaq.DAQProc.DAQProc", "modulename": "jupyterpidaq.DAQProc", "qualname": "DAQProc", "kind": "function", "doc": "

          This function is to be run in a separate thread to asynchronously\ncommunicate with the ADC board.

          \n\n
          Parameters
          \n\n
            \n
          • list whichchn: a list of dictionaries. Each dictionary is of the\nform:{'board': board_object, 'chnl': chnlID}.

          • \n
          • list gains: a list of the numerical gain for each channel.

          • \n
          • float avgtime: the averaging time in seconds for a data point.

          • \n
          • float timedelta: the target time between data points.

          • \n
          • pipe DAQconn: the connection pipe

          • \n
          • pipe DAQCTL: the control pipe

          • \n
          \n\n
          Returns
          \n\n
          \n

          Data is returned via the pipes.\n On the DAQCTL pipe this only returns 'done'\n On the DAQconn pipe a list of lists with data is returned.

          \n
          \n", "signature": "(whichchn, gains, avgtime, timedelta, DAQconn, DAQCTL):", "funcdef": "def"}, {"fullname": "jupyterpidaq.DAQinstance", "modulename": "jupyterpidaq.DAQinstance", "kind": "module", "doc": "

          \n"}, {"fullname": "jupyterpidaq.DAQinstance.DAQinstance", "modulename": "jupyterpidaq.DAQinstance", "qualname": "DAQinstance", "kind": "class", "doc": "

          \n"}, {"fullname": "jupyterpidaq.DAQinstance.DAQinstance.__init__", "modulename": "jupyterpidaq.DAQinstance", "qualname": "DAQinstance.__init__", "kind": "function", "doc": "

          Data Aquistion Instance (a run).

          \n\n
          Parameters
          \n\n
            \n
          • idno: id number you wish to use to keep track
          • \n
          • title: optional name
          • \n
          • ntraces: number of traces (default = 4) more than 4 easily\noverwhelms a pi4.
          • \n
          • kwargs: \n:ignore_skew: bool (default: True) if True only a single average\ncollection time will be recorded for each time in a multichannel\ndata collection. If False a separate set of time will be\nrecorded for each channel.
          • \n
          \n", "signature": "(idno, title='None', ntraces=4, **kwargs)"}, {"fullname": "jupyterpidaq.DAQinstance.DAQinstance.setupclick", "modulename": "jupyterpidaq.DAQinstance", "qualname": "DAQinstance.setupclick", "kind": "function", "doc": "

          \n", "signature": "(self, btn):", "funcdef": "def"}, {"fullname": "jupyterpidaq.DAQinstance.DAQinstance.setup", "modulename": "jupyterpidaq.DAQinstance", "qualname": "DAQinstance.setup", "kind": "function", "doc": "

          \n", "signature": "(self):", "funcdef": "def"}, {"fullname": "jupyterpidaq.DAQinstance.DAQinstance.collectclick", "modulename": "jupyterpidaq.DAQinstance", "qualname": "DAQinstance.collectclick", "kind": "function", "doc": "

          \n", "signature": "(self, btn):", "funcdef": "def"}, {"fullname": "jupyterpidaq.DAQinstance.DAQinstance.fillpandadf", "modulename": "jupyterpidaq.DAQinstance", "qualname": "DAQinstance.fillpandadf", "kind": "function", "doc": "

          \n", "signature": "(self):", "funcdef": "def"}, {"fullname": "jupyterpidaq.DAQinstance.DAQinstance.updatingplot", "modulename": "jupyterpidaq.DAQinstance", "qualname": "DAQinstance.updatingplot", "kind": "function", "doc": "

          Runs until a check of self.collectbtn.description does not return\n'Stop Collecting'. This would probably be more efficient if set a\nboolean.

          \n\n

          Parameters

          \n\n

          PLTconn: Pipe\n connection plotter end\nDAQconn: Pipe\n connection DAQ end\nPLTCTL: Pipe\n control pipe plotter end\nDAQCTL: Pipe\n control pipe DAQ end

          \n", "signature": "(self, PLTconn, PLTCTL):", "funcdef": "def"}, {"fullname": "jupyterpidaq.DAQinstance.Run", "modulename": "jupyterpidaq.DAQinstance", "qualname": "Run", "kind": "function", "doc": "

          Load a run from stored data or start a new run if the local file for\nthe run does not exist.

          \n\n

          Parameters

          \n\n

          name: str\n String name for the run. The data will be stored in a file of this\n name with the extension of .jpidaq.html.

          \n", "signature": "(name):", "funcdef": "def"}, {"fullname": "jupyterpidaq.DAQinstance.doRun", "modulename": "jupyterpidaq.DAQinstance", "qualname": "doRun", "kind": "function", "doc": "

          \n", "signature": "(whichrun):", "funcdef": "def"}, {"fullname": "jupyterpidaq.DAQinstance.update_runsdrp", "modulename": "jupyterpidaq.DAQinstance", "qualname": "update_runsdrp", "kind": "function", "doc": "

          \n", "signature": "():", "funcdef": "def"}, {"fullname": "jupyterpidaq.DAQinstance.showSelectedRunTable", "modulename": "jupyterpidaq.DAQinstance", "qualname": "showSelectedRunTable", "kind": "function", "doc": "

          \n", "signature": "(change):", "funcdef": "def"}, {"fullname": "jupyterpidaq.DAQinstance.showDataTable", "modulename": "jupyterpidaq.DAQinstance", "qualname": "showDataTable", "kind": "function", "doc": "

          Provides a menu to select which run. Then displays the run in a\n10 em high scrolling table. Selection menu is removed after choice\nis made.

          \n", "signature": "():", "funcdef": "def"}, {"fullname": "jupyterpidaq.DAQinstance.newCalculatedColumn", "modulename": "jupyterpidaq.DAQinstance", "qualname": "newCalculatedColumn", "kind": "function", "doc": "

          Uses jupyter-pandas-GUI.new_pandas_column_GUI to provide a GUI expression\ncomposer. This method finds the datasets and launches the GUI.

          \n", "signature": "():", "funcdef": "def"}, {"fullname": "jupyterpidaq.DAQinstance.newPlot", "modulename": "jupyterpidaq.DAQinstance", "qualname": "newPlot", "kind": "function", "doc": "

          Uses jupyter-pandas-GUI.plot_pandas_GUI to provide a GUI expression\ncomposer. This method finds the datasets and launches the GUI.

          \n", "signature": "():", "funcdef": "def"}, {"fullname": "jupyterpidaq.DAQinstance.newFit", "modulename": "jupyterpidaq.DAQinstance", "qualname": "newFit", "kind": "function", "doc": "

          Uses jupyter-pandas-GUI.fit_pandas_GUI to provide a GUI expression\ncomposer. This method finds the datasets and launches the GUI.

          \n", "signature": "():", "funcdef": "def"}, {"fullname": "jupyterpidaq.Sensors", "modulename": "jupyterpidaq.Sensors", "kind": "module", "doc": "

          This module wraps all the modules related to interpretting data from known\nsensors.

          \n"}, {"fullname": "jupyterpidaq.Sensors.sensors", "modulename": "jupyterpidaq.Sensors.sensors", "kind": "module", "doc": "

          \n"}, {"fullname": "jupyterpidaq.Sensors.sensors.to_reasonable_significant_figures", "modulename": "jupyterpidaq.Sensors.sensors", "qualname": "to_reasonable_significant_figures", "kind": "function", "doc": "

          This function will return value rounded to a reasonable number of\nsignificant figures based on the uncertainty. If you are doing this\nbased on the standard return from the raw voltage or the sensor\ndefinitions in this file it is recommend that this be the standard\ndeviation of the average, which will often provide about one more digit\nthan the standard deviation. This will provide a guard digit for further\ncomputations.

          \n\n
          Parameters
          \n\n
            \n
          • float value: the value to be rounded
          • \n
          • float uncertainty: the uncertainty.
          • \n
          \n\n

          :returns float:

          \n\n

          Returns: rounded_value a floating point number.

          \n", "signature": "(value, uncertainty):", "funcdef": "def"}, {"fullname": "jupyterpidaq.Sensors.sensors.to_reasonable_significant_figures_fast", "modulename": "jupyterpidaq.Sensors.sensors", "qualname": "to_reasonable_significant_figures_fast", "kind": "function", "doc": "

          This function will return values rounded to a reasonable number of\nsignificant figures based on the avg_std. This function requires fewer\ncompares so is a little more efficient than calling\nto_reasonable_significant_figures(value, uncertainty) for avg, std,\navg_std separately.

          \n\n
          Parameters
          \n\n
            \n
          • float avg: the average value
          • \n
          • float std: the standard deviation
          • \n
          • float avg_std: the estimated standard deviation in avg\n:returns list:
          • \n
          \n\n

          Returns: list of rounded values for each [avg, std, avg_std]

          \n", "signature": "(avg, std, avg_std):", "funcdef": "def"}, {"fullname": "jupyterpidaq.Sensors.sensors.listSensors", "modulename": "jupyterpidaq.Sensors.sensors", "qualname": "listSensors", "kind": "function", "doc": "

          Provides a list of the sensor classes provided by this file. The list must\nbe manually updated with each new class.

          \n\n
          Returns
          \n\n
          \n

          list of classnames

          \n
          \n", "signature": "():", "funcdef": "def"}, {"fullname": "jupyterpidaq.Sensors.sensors.RawAtoD", "modulename": "jupyterpidaq.Sensors.sensors", "qualname": "RawAtoD", "kind": "class", "doc": "

          This is the base sensor class which all sensors should extend. See how to\ndo this properly using one of the examples below.\nThis class contains definitions for the raw AtoD return in volts. The\ndigital values are not used as the AtoD may have a builtin pre-amp,\nso a given digital value has different meanings depending upon the pre-amp\nsetting.

          \n"}, {"fullname": "jupyterpidaq.Sensors.sensors.RawAtoD.__init__", "modulename": "jupyterpidaq.Sensors.sensors", "qualname": "RawAtoD.__init__", "kind": "function", "doc": "

          This init should be called first in the init section of any class\nextending this class (e.g. super().__init__(Vdd)). Then set\nself.name and self.vendor to the proper values. Append units\nspecific to the sensor to self.units. The parameter Vdd must be\nsupplied upon initialization because the output voltage of some\nsensors depends on Vdd.

          \n\n
          Parameters
          \n\n
            \n
          • float Vdd: the voltage supplied to the sensor by the A-to-D\nboard in case the sensor output depends on this.
          • \n
          \n", "signature": "(Vdd)"}, {"fullname": "jupyterpidaq.Sensors.sensors.RawAtoD.getname", "modulename": "jupyterpidaq.Sensors.sensors", "qualname": "RawAtoD.getname", "kind": "function", "doc": "

          Provides a string name for the sensor

          \n\n
          Returns
          \n\n
          \n

          string containing the sensor name

          \n
          \n", "signature": "(self):", "funcdef": "def"}, {"fullname": "jupyterpidaq.Sensors.sensors.RawAtoD.getvendor", "modulename": "jupyterpidaq.Sensors.sensors", "qualname": "RawAtoD.getvendor", "kind": "function", "doc": "

          Provides a string name for the sensor vendor/manufacturer

          \n\n
          Returns
          \n\n
          \n

          string containing the vendor/manufacturer name

          \n
          \n", "signature": "(self):", "funcdef": "def"}, {"fullname": "jupyterpidaq.Sensors.sensors.RawAtoD.getunits", "modulename": "jupyterpidaq.Sensors.sensors", "qualname": "RawAtoD.getunits", "kind": "function", "doc": "

          Provides the string names for the available units for this sensor.\nThese string names are also the functions within this class that\nreturn the measurement in those units.

          \n\n
          Returns
          \n\n
          \n

          units a list of strings.

          \n
          \n", "signature": "(self):", "funcdef": "def"}, {"fullname": "jupyterpidaq.Sensors.sensors.RawAtoD.V", "modulename": "jupyterpidaq.Sensors.sensors", "qualname": "RawAtoD.V", "kind": "function", "doc": "

          It is not really necessary to call this function because it just\nreturns the same values that are passed to it.\nIt is provided for consistency with the way sensors units are defined.

          \n\n
          Parameters
          \n\n
            \n
          • v_avg: v_avg: average voltage from A-to-D
          • \n
          • v_std: standard deviation of the A-to-D measurements
          • \n
          • avg_std: estimate of the standard deviation of v_avg
          • \n
          • float avg_vdd: simultaneously measured average Vdd.
          • \n
          \n\n
          Returns
          \n\n
          \n

          [v_avg, v_std, avg_std]

          \n
          \n", "signature": "(self, v_avg, v_std, avg_std, avg_vdd):", "funcdef": "def"}, {"fullname": "jupyterpidaq.Sensors.sensors.RawAtoD.mV", "modulename": "jupyterpidaq.Sensors.sensors", "qualname": "RawAtoD.mV", "kind": "function", "doc": "

          Convert the raw AtoD voltage to mV.

          \n\n
          Parameters
          \n\n
            \n
          • v_avg: v_avg: average voltage from A-to-D
          • \n
          • v_std: standard deviation of the A-to-D measurements
          • \n
          • avg_std: estimate of the standard deviation of v_avg
          • \n
          • float avg_vdd: simultaneously measured average Vdd.
          • \n
          \n\n
          Returns
          \n\n
          \n

          [v_avg, v_std, avg_std] converted to mV

          \n
          \n", "signature": "(self, v_avg, v_std, avg_std, avg_vdd):", "funcdef": "def"}, {"fullname": "jupyterpidaq.Sensors.sensors.BuiltInThermistor", "modulename": "jupyterpidaq.Sensors.sensors", "qualname": "BuiltInThermistor", "kind": "class", "doc": "

          This class contains the definitions for builtin thermistor.

          \n", "bases": "RawAtoD"}, {"fullname": "jupyterpidaq.Sensors.sensors.BuiltInThermistor.__init__", "modulename": "jupyterpidaq.Sensors.sensors", "qualname": "BuiltInThermistor.__init__", "kind": "function", "doc": "

          This init should be called first in the init section of any class\nextending this class (e.g. super().__init__(Vdd)). Then set\nself.name and self.vendor to the proper values. Append units\nspecific to the sensor to self.units. The parameter Vdd must be\nsupplied upon initialization because the output voltage of some\nsensors depends on Vdd.

          \n\n
          Parameters
          \n\n
            \n
          • float Vdd: the voltage supplied to the sensor by the A-to-D\nboard in case the sensor output depends on this.
          • \n
          \n", "signature": "(Vdd)"}, {"fullname": "jupyterpidaq.Sensors.sensors.BuiltInThermistor.K", "modulename": "jupyterpidaq.Sensors.sensors", "qualname": "BuiltInThermistor.K", "kind": "function", "doc": "

          The returned values are in K. It is assumed that the distribution is\nsymmetric guassian even in K. This may not be true, but still gives\na reasonable estimate of the standard deviation.

          \n\n
          Parameters
          \n\n
            \n
          • v_avg: average voltage from sensor.
          • \n
          • v_std: standard deviation of voltage from sensor.
          • \n
          • avg_std: estimated standard deviation of the avg.
          • \n
          • float avg_vdd: simultaneously measured average Vdd.
          • \n
          \n\n
          Returns
          \n\n
          \n

          \n
          \n\n

          Returns: list [K_avg, K_std, K_avg_std]\n [average temperature in K,\n standard deviation of temperature in K,\n estimated standard deviation of the average temperature].

          \n", "signature": "(self, v_avg, v_std, avg_std, avg_vdd):", "funcdef": "def"}, {"fullname": "jupyterpidaq.Sensors.sensors.BuiltInThermistor.C", "modulename": "jupyterpidaq.Sensors.sensors", "qualname": "BuiltInThermistor.C", "kind": "function", "doc": "

          The returned values are in deg C. It is assumed that the distribution\nis symmetric guassian even in deg C. This may not be true, but still\ngives a reasonable estimate of the standard deviation.

          \n\n
          Parameters
          \n\n
            \n
          • v_avg: average voltage from sensor.
          • \n
          • v_std: standard deviation of voltage from sensor.
          • \n
          • avg_std: estimated standard deviation of the avg.
          • \n
          • float avg_vdd: simultaneously measured average Vdd.
          • \n
          \n\n
          Returns
          \n\n
          \n

          \n
          \n\n

          Returns: list [C_avg, C_std, C_avg_std]\n [average temperature in C, standard deviation of temperature in C,\n estimated standard deviation of the average temperature].

          \n", "signature": "(self, v_avg, v_std, avg_std, avg_vdd):", "funcdef": "def"}, {"fullname": "jupyterpidaq.Sensors.sensors.BuiltInThermistor.F", "modulename": "jupyterpidaq.Sensors.sensors", "qualname": "BuiltInThermistor.F", "kind": "function", "doc": "

          The returned values are in deg F. It is assumed that the distribution\nis symmetric guassian even in deg F. This may not be true, but still\ngives a reasonable estimate of the standard deviation.

          \n\n
          Parameters
          \n\n
            \n
          • float v_avg: average voltage from sensor.
          • \n
          • float v_std: standard deviation of voltage from sensor.
          • \n
          • float avg_std: estimated standard deviation of the avg.
          • \n
          • float avg_vdd: simultaneously measured average Vdd.\n:returns list:
          • \n
          \n\n

          Returns: list [F_avg, F_std, F_avg_std]\n [average temperature in F, standard deviation of temperature in F,\n estimated standard deviation of the average temperature].

          \n", "signature": "(self, v_avg, v_std, avg_std, avg_vdd):", "funcdef": "def"}, {"fullname": "jupyterpidaq.Sensors.sensors.VernierSSTemp", "modulename": "jupyterpidaq.Sensors.sensors", "qualname": "VernierSSTemp", "kind": "class", "doc": "

          This class contains the definitions for Vernier Stainless Steel Temperature\nProbe. A 20K thermistor.

          \n", "bases": "RawAtoD"}, {"fullname": "jupyterpidaq.Sensors.sensors.VernierSSTemp.__init__", "modulename": "jupyterpidaq.Sensors.sensors", "qualname": "VernierSSTemp.__init__", "kind": "function", "doc": "

          This init should be called first in the init section of any class\nextending this class (e.g. super().__init__(Vdd)). Then set\nself.name and self.vendor to the proper values. Append units\nspecific to the sensor to self.units. The parameter Vdd must be\nsupplied upon initialization because the output voltage of some\nsensors depends on Vdd.

          \n\n
          Parameters
          \n\n
            \n
          • float Vdd: the voltage supplied to the sensor by the A-to-D\nboard in case the sensor output depends on this.
          • \n
          \n", "signature": "(Vdd)"}, {"fullname": "jupyterpidaq.Sensors.sensors.VernierSSTemp.K", "modulename": "jupyterpidaq.Sensors.sensors", "qualname": "VernierSSTemp.K", "kind": "function", "doc": "

          The returned values are in K. It is assumed that the distribution is\nsymmetric guassian even in K. This may not be true, but still gives\na reasonable estimate of the standard deviation.

          \n\n
          Parameters
          \n\n
            \n
          • float v_avg: average voltage from sensor.
          • \n
          • float v_std: standard deviation of voltage from sensor.
          • \n
          • float avg_std: estimated standard deviation of the avg.
          • \n
          • float avg_vdd: simultaneously measured average Vdd.\n:returns list:
          • \n
          \n\n

          Returns list [K_avg, K_std, K_avg_std]\n [average temperature in K, standard deviation of temperature in K,\n estimated standard deviation of the average temperature].

          \n", "signature": "(self, v_avg, v_std, avg_std, avg_vdd):", "funcdef": "def"}, {"fullname": "jupyterpidaq.Sensors.sensors.VernierSSTemp.C", "modulename": "jupyterpidaq.Sensors.sensors", "qualname": "VernierSSTemp.C", "kind": "function", "doc": "

          The returned values are in deg C. It is assumed that the distribution\nis symmetric guassian even in deg C. This may not be true, but still\ngives a reasonable estimate of the standard deviation.

          \n\n
          Parameters
          \n\n
            \n
          • float v_avg: average voltage from sensor.
          • \n
          • float v_std: standard deviation of voltage from sensor.
          • \n
          • float avg_std: estimated standard deviation of the avg.
          • \n
          • float avg_vdd: simultaneously measured average Vdd.
          • \n
          \n\n
          Returns
          \n\n
          \n

          \n
          \n\n

          Returns: C_avg, C_std, C_avg_std\n average temperature in C, standard deviation of temperature in C,\n estimated standard deviation of the average temperature.

          \n", "signature": "(self, v_avg, v_std, avg_std, avg_vdd):", "funcdef": "def"}, {"fullname": "jupyterpidaq.Sensors.sensors.VernierSSTemp.F", "modulename": "jupyterpidaq.Sensors.sensors", "qualname": "VernierSSTemp.F", "kind": "function", "doc": "

          The returned values are in deg F. It is assumed that the\ndistribution is symmetric guassian even in deg F. This may not be\ntrue, but still gives a reasonable estimate of the standard\ndeviation.

          \n\n
          Parameters
          \n\n
            \n
          • float v_avg: average voltage from sensor.
          • \n
          • float v_std: standard deviation of voltage from sensor.
          • \n
          • float avg_std: estimated standard deviation of the avg.
          • \n
          • float avg_vdd: simultaneously measured average Vdd.
          • \n
          \n\n
          Returns
          \n\n
          \n

          \n
          \n\n

          Returns: F_avg, F_std, F_avg_std\n average temperature in F, standard deviation of\n temperature in F, estimated standard deviation of the average\n temperature.

          \n", "signature": "(self, v_avg, v_std, avg_std, avg_vdd):", "funcdef": "def"}, {"fullname": "jupyterpidaq.Sensors.sensors.VernierGasP", "modulename": "jupyterpidaq.Sensors.sensors", "qualname": "VernierGasP", "kind": "class", "doc": "

          This class contains the definitions for Vernier absolute gas pressure\nsensor, GPS-BTA (post 2011 manufacture).

          \n", "bases": "RawAtoD"}, {"fullname": "jupyterpidaq.Sensors.sensors.VernierGasP.__init__", "modulename": "jupyterpidaq.Sensors.sensors", "qualname": "VernierGasP.__init__", "kind": "function", "doc": "

          This init should be called first in the init section of any class\nextending this class (e.g. super().__init__(Vdd)). Then set\nself.name and self.vendor to the proper values. Append units\nspecific to the sensor to self.units. The parameter Vdd must be\nsupplied upon initialization because the output voltage of some\nsensors depends on Vdd.

          \n\n
          Parameters
          \n\n
            \n
          • float Vdd: the voltage supplied to the sensor by the A-to-D\nboard in case the sensor output depends on this.
          • \n
          \n", "signature": "(Vdd)"}, {"fullname": "jupyterpidaq.Sensors.sensors.VernierGasP.Pa", "modulename": "jupyterpidaq.Sensors.sensors", "qualname": "VernierGasP.Pa", "kind": "function", "doc": "
          Parameters
          \n\n
            \n
          • float v_avg: average raw voltage
          • \n
          • float v_std: standard deviation of the raw voltage
          • \n
          • float avg_std: estimated standard deviation of v_avg
          • \n
          • float avg_vdd: the Vdd measured simultaneously with v_avg (not\nused)
          • \n
          \n\n
          Returns
          \n\n
          \n

          \n
          \n\n

          Returns: P_avg, P_std, P_avg_std all in Pascals

          \n", "signature": "(self, v_avg, v_std, avg_std, avg_vdd):", "funcdef": "def"}, {"fullname": "jupyterpidaq.Sensors.sensors.VernierGasP.kPa", "modulename": "jupyterpidaq.Sensors.sensors", "qualname": "VernierGasP.kPa", "kind": "function", "doc": "
          Parameters
          \n\n
            \n
          • float v_avg: average raw voltage
          • \n
          • float v_std: standard deviation of the raw voltage
          • \n
          • float avg_std: estimated standard deviation of v_avg
          • \n
          • float avg_vdd: the Vdd measured simultaneously with v_avg (not\nused)
          • \n
          \n\n
          Returns
          \n\n
          \n

          \n
          \n\n

          Returns: P_avg, P_std, P_avg_std all in kiloPascals

          \n", "signature": "(self, v_avg, v_std, avg_std, avg_vdd):", "funcdef": "def"}, {"fullname": "jupyterpidaq.Sensors.sensors.VernierGasP.Bar", "modulename": "jupyterpidaq.Sensors.sensors", "qualname": "VernierGasP.Bar", "kind": "function", "doc": "
          Parameters
          \n\n
            \n
          • float v_avg: average raw voltage
          • \n
          • float v_std: standard deviation of the raw voltage
          • \n
          • float avg_std: estimated standard deviation of v_avg
          • \n
          • float avg_vdd: the Vdd measured simultaneously with v_avg (not\nused)
          • \n
          \n\n
          Returns
          \n\n
          \n

          \n
          \n\n

          Returns: P_avg, P_std, P_avg_std all in Bars

          \n", "signature": "(self, v_avg, v_std, avg_std, avg_vdd):", "funcdef": "def"}, {"fullname": "jupyterpidaq.Sensors.sensors.VernierGasP.Torr", "modulename": "jupyterpidaq.Sensors.sensors", "qualname": "VernierGasP.Torr", "kind": "function", "doc": "
          Parameters
          \n\n
            \n
          • float v_avg: average raw voltage
          • \n
          • float v_std: standard deviation of the voltage measurements
          • \n
          • float avg_std: estimate of the standard deviation of the average
          • \n
          • float avg_vdd: the Vdd measured simultaneously with v_avg (\nnot used)
          • \n
          \n\n
          Returns
          \n\n
          \n

          \n
          \n\n

          Returns: P_avg, P_std, P_avg_std all in Torr

          \n", "signature": "(self, v_avg, v_std, avg_std, avg_vdd):", "funcdef": "def"}, {"fullname": "jupyterpidaq.Sensors.sensors.VernierGasP.mmHg", "modulename": "jupyterpidaq.Sensors.sensors", "qualname": "VernierGasP.mmHg", "kind": "function", "doc": "
          Parameters
          \n\n
            \n
          • float v_avg: average raw voltage
          • \n
          • float v_std: standard deviation of the voltage measurements
          • \n
          • float avg_std: estimate of the standard deviation of the average
          • \n
          • float avg_vdd: the Vdd measured simultaneously with v_avg (\nnot used)
          • \n
          \n\n
          Returns
          \n\n
          \n

          \n
          \n\n

          Returns: P_avg, P_std, P_avg_std all in mmHg

          \n", "signature": "(self, v_avg, v_std, avg_std, avg_vdd):", "funcdef": "def"}, {"fullname": "jupyterpidaq.Sensors.sensors.VernierGasP.atm", "modulename": "jupyterpidaq.Sensors.sensors", "qualname": "VernierGasP.atm", "kind": "function", "doc": "
          Parameters
          \n\n
            \n
          • float v_avg: average raw voltage
          • \n
          • float v_std: standard deviation of the voltage measurements
          • \n
          • float avg_std: estimate of the standard deviation of the average
          • \n
          • float avg_vdd: the Vdd measured simultaneously with v_avg (\nnot used)
          • \n
          \n\n
          Returns
          \n\n
          \n

          \n
          \n\n

          Returns: P_avg, P_std, P_avg_std all in atm

          \n", "signature": "(self, v_avg, v_std, avg_std, avg_vdd):", "funcdef": "def"}, {"fullname": "jupyterpidaq.Sensors.sensors.VernierGasP_OLD", "modulename": "jupyterpidaq.Sensors.sensors", "qualname": "VernierGasP_OLD", "kind": "class", "doc": "

          This class contains the definitions for Vernier absolute gas pressure\nsensor, GPS-BTA (pre 2011 manufacture. Label does not depict a caliper\nwith the registered trademark symbol).

          \n", "bases": "RawAtoD"}, {"fullname": "jupyterpidaq.Sensors.sensors.VernierGasP_OLD.__init__", "modulename": "jupyterpidaq.Sensors.sensors", "qualname": "VernierGasP_OLD.__init__", "kind": "function", "doc": "

          This init should be called first in the init section of any class\nextending this class (e.g. super().__init__(Vdd)). Then set\nself.name and self.vendor to the proper values. Append units\nspecific to the sensor to self.units. The parameter Vdd must be\nsupplied upon initialization because the output voltage of some\nsensors depends on Vdd.

          \n\n
          Parameters
          \n\n
            \n
          • float Vdd: the voltage supplied to the sensor by the A-to-D\nboard in case the sensor output depends on this.
          • \n
          \n", "signature": "(Vdd)"}, {"fullname": "jupyterpidaq.Sensors.sensors.VernierGasP_OLD.Pa", "modulename": "jupyterpidaq.Sensors.sensors", "qualname": "VernierGasP_OLD.Pa", "kind": "function", "doc": "
          Parameters
          \n\n
            \n
          • float v_avg: average raw voltage
          • \n
          • float v_std: standard deviation of the raw voltage
          • \n
          • float avg_std: estimated standard deviation of v_avg
          • \n
          • float avg_vdd: the Vdd measured simultaneously with v_avg (not\nused)
          • \n
          \n\n
          Returns
          \n\n
          \n

          \n
          \n\n

          Returns: P_avg, P_std, P_avg_std all in Pascals

          \n", "signature": "(self, v_avg, v_std, avg_std, avg_vdd):", "funcdef": "def"}, {"fullname": "jupyterpidaq.Sensors.sensors.VernierGasP_OLD.kPa", "modulename": "jupyterpidaq.Sensors.sensors", "qualname": "VernierGasP_OLD.kPa", "kind": "function", "doc": "
          Parameters
          \n\n
            \n
          • float v_avg: average raw voltage
          • \n
          • float v_std: standard deviation of the raw voltage
          • \n
          • float avg_std: estimated standard deviation of v_avg
          • \n
          • float avg_vdd: the Vdd measured simultaneously with v_avg (not\nused)
          • \n
          \n\n
          Returns
          \n\n
          \n

          \n
          \n\n

          Returns: P_avg, P_std, P_avg_std all in kiloPascals

          \n", "signature": "(self, v_avg, v_std, avg_std, avg_vdd):", "funcdef": "def"}, {"fullname": "jupyterpidaq.Sensors.sensors.VernierGasP_OLD.Bar", "modulename": "jupyterpidaq.Sensors.sensors", "qualname": "VernierGasP_OLD.Bar", "kind": "function", "doc": "
          Parameters
          \n\n
            \n
          • float v_avg: average raw voltage
          • \n
          • float v_std: standard deviation of the raw voltage
          • \n
          • float avg_std: estimated standard deviation of v_avg
          • \n
          • float avg_vdd: the Vdd measured simultaneously with v_avg (not\nused)
          • \n
          \n\n
          Returns
          \n\n
          \n

          \n
          \n\n

          Returns: P_avg, P_std, P_avg_std all in Bars

          \n", "signature": "(self, v_avg, v_std, avg_std, avg_vdd):", "funcdef": "def"}, {"fullname": "jupyterpidaq.Sensors.sensors.VernierGasP_OLD.Torr", "modulename": "jupyterpidaq.Sensors.sensors", "qualname": "VernierGasP_OLD.Torr", "kind": "function", "doc": "
          Parameters
          \n\n
            \n
          • float v_avg: average raw voltage
          • \n
          • float v_std: standard deviation of the voltage measurements
          • \n
          • float avg_std: estimate of the standard deviation of the average
          • \n
          • float avg_vdd: the Vdd measured simultaneously with v_avg (\nnot used)
          • \n
          \n\n
          Returns
          \n\n
          \n

          \n
          \n\n

          Returns: P_avg, P_std, P_avg_std all in Torr

          \n", "signature": "(self, v_avg, v_std, avg_std, avg_vdd):", "funcdef": "def"}, {"fullname": "jupyterpidaq.Sensors.sensors.VernierGasP_OLD.mmHg", "modulename": "jupyterpidaq.Sensors.sensors", "qualname": "VernierGasP_OLD.mmHg", "kind": "function", "doc": "
          Parameters
          \n\n
            \n
          • float v_avg: average raw voltage
          • \n
          • float v_std: standard deviation of the voltage measurements
          • \n
          • float avg_std: estimate of the standard deviation of the average
          • \n
          • float avg_vdd: the Vdd measured simultaneously with v_avg (\nnot used)
          • \n
          \n\n
          Returns
          \n\n
          \n

          \n
          \n\n

          Returns: P_avg, P_std, P_avg_std all in mmHg

          \n", "signature": "(self, v_avg, v_std, avg_std, avg_vdd):", "funcdef": "def"}, {"fullname": "jupyterpidaq.Sensors.sensors.VernierGasP_OLD.atm", "modulename": "jupyterpidaq.Sensors.sensors", "qualname": "VernierGasP_OLD.atm", "kind": "function", "doc": "
          Parameters
          \n\n
            \n
          • float v_avg: average raw voltage
          • \n
          • float v_std: standard deviation of the voltage measurements
          • \n
          • float avg_std: estimate of the standard deviation of the average
          • \n
          • float avg_vdd: the Vdd measured simultaneously with v_avg (\nnot used)
          • \n
          \n\n
          Returns
          \n\n
          \n

          \n
          \n\n

          Returns: P_avg, P_std, P_avg_std all in atm

          \n", "signature": "(self, v_avg, v_std, avg_std, avg_vdd):", "funcdef": "def"}, {"fullname": "jupyterpidaq.Sensors.sensors.VernierpH", "modulename": "jupyterpidaq.Sensors.sensors", "qualname": "VernierpH", "kind": "class", "doc": "

          This class contains the definitions for Vernier standard pH\nsensor, PH-BTA.

          \n", "bases": "RawAtoD"}, {"fullname": "jupyterpidaq.Sensors.sensors.VernierpH.__init__", "modulename": "jupyterpidaq.Sensors.sensors", "qualname": "VernierpH.__init__", "kind": "function", "doc": "

          This init should be called first in the init section of any class\nextending this class (e.g. super().__init__(Vdd)). Then set\nself.name and self.vendor to the proper values. Append units\nspecific to the sensor to self.units. The parameter Vdd must be\nsupplied upon initialization because the output voltage of some\nsensors depends on Vdd.

          \n\n
          Parameters
          \n\n
            \n
          • float Vdd: the voltage supplied to the sensor by the A-to-D\nboard in case the sensor output depends on this.
          • \n
          \n", "signature": "(Vdd)"}, {"fullname": "jupyterpidaq.Sensors.sensors.VernierpH.pH", "modulename": "jupyterpidaq.Sensors.sensors", "qualname": "VernierpH.pH", "kind": "function", "doc": "
          Parameters
          \n\n
            \n
          • float v_avg: average raw voltage
          • \n
          • float v_std: standard deviation of the raw voltage
          • \n
          • float avg_std: estimated standard deviation of v_avg
          • \n
          • float avg_vdd: the Vdd measured simultaneously with v_avg (not\nused)
          • \n
          \n\n
          Returns
          \n\n
          \n

          \n
          \n\n

          Returns: pH_avg, pH_std, pH_avg_std all in pH units

          \n", "signature": "(self, v_avg, v_std, avg_std, avg_vdd):", "funcdef": "def"}, {"fullname": "jupyterpidaq.Sensors.sensors.VernierFlatpH", "modulename": "jupyterpidaq.Sensors.sensors", "qualname": "VernierFlatpH", "kind": "class", "doc": "

          This class contains the definitions for Vernier flat tris-compatible pH\nsensor, FPH-BTA.

          \n", "bases": "RawAtoD"}, {"fullname": "jupyterpidaq.Sensors.sensors.VernierFlatpH.__init__", "modulename": "jupyterpidaq.Sensors.sensors", "qualname": "VernierFlatpH.__init__", "kind": "function", "doc": "

          This init should be called first in the init section of any class\nextending this class (e.g. super().__init__(Vdd)). Then set\nself.name and self.vendor to the proper values. Append units\nspecific to the sensor to self.units. The parameter Vdd must be\nsupplied upon initialization because the output voltage of some\nsensors depends on Vdd.

          \n\n
          Parameters
          \n\n
            \n
          • float Vdd: the voltage supplied to the sensor by the A-to-D\nboard in case the sensor output depends on this.
          • \n
          \n", "signature": "(Vdd)"}, {"fullname": "jupyterpidaq.Sensors.sensors.VernierFlatpH.pH", "modulename": "jupyterpidaq.Sensors.sensors", "qualname": "VernierFlatpH.pH", "kind": "function", "doc": "
          Parameters
          \n\n
            \n
          • float v_avg: average raw voltage
          • \n
          • float v_std: standard deviation of the raw voltage
          • \n
          • float avg_std: estimated standard deviation of v_avg
          • \n
          • float avg_vdd: the Vdd measured simultaneously with v_avg (not\nused)
          • \n
          \n\n
          Returns
          \n\n
          \n

          \n
          \n\n

          Returns: pH_avg, pH_std, pH_avg_std all in pH units

          \n", "signature": "(self, v_avg, v_std, avg_std, avg_vdd):", "funcdef": "def"}]; + /** pdoc search index */const docs = [{"fullname": "jupyterpidaq", "modulename": "jupyterpidaq", "kind": "module", "doc": "

          User Guide

          \n\n

          Introduction | \nUsage

          \n\n

          Introduction

          \n\n

          This software allows GUI (Graphical User Interface) driven live collection, \nplotting and analysis of digitized data inside a Jupyter notebook using the \nfollowing A-to-D interfaces:

          \n\n

          on a Raspberry Pi:

          \n\n\n\n

          on MacOS and Windows:

          \n\n\n\n

          demo mode on anything Jupyter runs on

          \n\n
            \n
          • A demo mode will run on any computer with a Jupyter notebook install and\nPython 3.6+. Example notebooks can be found in the \"usage_examples\" folder.
          • \n
          \n\n

          Usage

          \n\n

          Launch the software | \nInitialize data acquisiton tools | \nCollect data |\nDisplaying a Data Table | \nPlotting data | \nAnalyzing data

          \n\n

          Starting JupyterPiDAQ

          \n\n

          A working Jupyter notebook or Jupyter lab installation with JupyterPiDAQ \ninstalled is required. If you need to install the software see the Installation \nInstructions. There are two common ways this may be set \nup, that lead to slightly different steps for starting the software:

          \n\n
            \n
          1. A special kernel may be set up that can be used in any Jupyter notebook \ninstall for the current user (see the very end of the\nInstallation Instructions). \n
              \n
            • In this case launch\nJupyter, in whichever directory you want to work, using the \ncommand: jupyter nbclassic (for the old notebook interface) or jupyter \nlab (for the newer lab interface).
            • \n
            • Open a new notebook and choose the kernel \nfor JupyterPiDAQ. The kernel name will depend upon what was chosen \nduring installation.
            • \n
          2. \n
          3. Only for use within the directory structure of the virtual environment \nthat was set up for the software. \n
              \n
            • In this case you must navigate to the \ndirectory of the virtual environment using the cd command before \nstarting the software.
            • \n
            • Then enter the virtual environment with the command pipenv shell. \nThis assumes you set up pipenv as described in the \nInstallation instructions.
            • \n
            • Launch Jupyter using the command: jupyter nbclassic (for the old \nnotebook interface) or jupyter lab (for the newer lab interface).
            • \n
            • Open a new python notebook.
            • \n
          4. \n
          \n\n

          Initialize the Data Acquisition Tools

          \n\n

          Initialize the data acquisition tools by putting the statement from \njupyterpidaq.DAQinstance import * into the first cell and clicking on the \n'Run' button. The package loads supporting packages (numpy, pandas, plotly,\netc...) and searches for compatible hardware. On a Raspberry Pi this takes a\nnumber of seconds. If no compatible A-to-D boards are found, demo mode is used.\nIn demo mode the A-to-D board is simulated by a random number generator.

          \n\n

          When setup is done a new menu appears at the end of the menubar (figure 1).

          \n\n

          \"DAQ

          \n\n

          Figure 1: The menu created once the data acquisition software is \ninitialized. Currently NOT available in Jupyter Lab.

          \n\n

          The menu options insert jupyter widget based GUIs for starting a run,\ndisplaying the data as tables or plots, composing an expression to calculate\na new column in a DataFrame, or fitting data.

          \n\n

          Collecting data

          \n\n

          Using the Menu

          \n\n

          From the \"DAQ Commands\" menu select \"Insert New Run After Selection...\" or \n\"Append New Run to End...\". The first will insert the code to set up \ndata collection for a run in the cell below the currently selected cell. \nThe second will append this to the end of the notebook. When the cell is \nrun it will generate \na GUI that looks like the figure 2, below. Fill in the information to define \nwhat you wish to do (see below figure 2 for more details).

          \n\n

          Using a Command

          \n\n

          In the cell you wish to collect the data enter the command \nRun(\"desired_dataset_name\"), where you replace desired_dataset_name with the \nstring you want for this run. Replacing spaces with _ will prevent issues, \nespecially on Windows. After entering the command, run the cell. This will \ngenerate the GUI shown in figure 2.

          \n\n

          \"New

          \n\n

          Figure 2: Image of the GUI for setting up a data collection run.

          \n\n
            \n
          1. Give the run a title/name using the first textbox.
          2. \n
          3. You can collect up to four (4) data traces at once. Two different data \ntraces can display the same analog-to-digital channel, but in different \nunits (e.g. you could record both the raw voltage and temperature from a \nthermistor). Each trace is activated by selecting its checkbox.
          4. \n
          5. Once a trace is activated you can give it a title and must select: the \ndata acquisition board, channel, sensor, units and gain from the \navailable drop-down menus.
          6. \n
          7. Once all your traces are set up decide whether they should be displayed in \nmultiple stacked graphs or all on the same graph. Uncheck the box to \ndisplay all on one graph.
          8. \n
          9. Select the data collection rate (20 Hz is currently the maximum rate, but \non a Raspberry Pi you may not be able to sustain more than 5 Hz).
          10. \n
          11. When everything is set the way you wish, click on the \"Set Parameters\" \nbutton. The collection parameters will be displayed and a button to \nstart the data collection will appear. You may have to scroll back up to \nthe display as Jupyter sometimes jumps too far down when a cell is updated.
          12. \n
          13. The \"start\" button will convert to a \"stop\" button once data collection \nis started. The data graph(s) will update at roughly 1 Hz, so you can \nmonitor the progress of the data collection.
          14. \n
          15. Click the \"stop\" button to end data collection. It can take \na while to stop if the data collection has got ahead of the graphic \ndisplay of the data.
          16. \n
          17. Once collection is stopped you will see a plot or plots of the completed \ndata collection and the name of the .html file the raw data has been \nbacked up to.
          18. \n
          19. Should you accidentally clear the output of a completed collection cell,\nrerunning it will regenerate the display as long as you have not moved \nthe raw data backup file.
          20. \n
          \n\n

          Displaying data

          \n\n

          In a table

          \n\n

          Selecting the \"Show data in table...\" option in the \"DAQ Command\" menu will \ninsert a cell immediately below the currently selected cell displaying a \nwidget in which you can select which data set to display. The command \nequivalent is showDataTable().

          \n\n

          Plotting data

          \n\n

          Selecting the \"Insert new plot after selection...\" option in the menu will\ninsert two cells immediately below the currently selected cell. The first cell\nwill be used to generate a GUI to lead you through creation of the code to\ngenerate the plot. The second cell is where the plot creation code is \ngenerated. The first tab of this GUI looks like figure 3. The command \nequivalent is newPlot().

          \n\n

          \"Plot

          \n\n

          Figure 3: Image of the first tab in the four tab (4 step) Pandas Plot \nComposer. More information in the Pandas_GUI\ndocumentation.

          \n\n

          It is best to do the tabs in order. The notices in red will try to \nwarn you of errors or oversights. The \"Instructions\" accordian can be \nexpanded to get more specific information about how to use each tab.

          \n\n

          You can get more sophisticated control of \nyour plot by editing the code produced by this GUI. See the Plotly \nFigureWidget Instructions and the \nexample Jupyter notebooks referenced there for more information.

          \n\n

          The GUI destroys itself once you complete step 4.

          \n\n

          Analyzing data

          \n\n

          Calculating a new column

          \n\n

          Selecting \"Calculate new column...\" from the menu will add two cells\nimmediately below the selected cell. The first cell will create the GUI to\nlead you through creation of the code to calculate the new column. The second\ncell is where the code is built. The first tab of the GUI looks like \nfigure 4. The command equivalent is newCalculatedColumn().

          \n\n

          \"New

          \n\n

          Figure 4: Image of the first tab in the four tab (4 step) Pandas New \nCalculated Column Composer. More information in the Pandas_GUI\ndocumentation.

          \n\n

          Do the tabs in order. You can perform more complex manipulations than built \ninto the GUI by editing the code generated by this GUI.

          \n\n

          The GUI destroys itself once you complete step 4.

          \n\n

          Fitting data

          \n\n

          A GUI for defining simple fits (linear, polynomial, exponential decay, sine \nand Gaussian) can be launched by selecting \"Insert new Fit after selection..\n.\" from the menu. This will create the GUI to lead you through selecting \nand fitting the data. The code is created in the cell immediately below the \nGUI. The command equivalent is newFit().

          \n\n

          \"Fit

          \n\n

          Figure 5: Image of the first tab in the Pandas Fit Composer.\nMore information in the Pandas_GUI\ndocumentation.

          \n\n

          Installation

          \n\n

          Initial setup: On Raspberry Pi | \nOn non-Pi Systems

          \n\n

          Final Set up

          \n\n

          Raspberry Pi Initial Setup

          \n\n

          Unless you only want to run in Demo mode make sure you have one of the \ncompatible interface boards installed. The current options are:

          \n\n
            \n
          • Adafruit compliant ADS1115 boards \n(example,\nalso available from other vendors);
          • \n
          • The π-Plates DAQC2 plate.
          • \n
          • If you wish to use different interfaces see the Development\nNotes\n and the Boards subpackage of jupyterpidaq for examples and \n information on how to define the code interface for a board.
          • \n
          \n\n

          OS specific: Ubuntu on Pi | \nRaspberrian on Pi |\nMacOS | Windows

          \n\n

          Ubuntu on Pi

          \n\n

          By default in Ubuntu 20.04 for Pis the gpio and spi groups do not exist.\nThe i2c group does (not always).

          \n\n
            \n
          1. Make sure that the following packages are installed rpi.gpio-common \npython3-pigpio python3-gpiozero python3-rpi.gpio.
          2. \n
          3. You can avoid having to create a gpio group, by assigning users who need\n gpio access to the dialout group. Check that /dev/gpiomem is part of that \ngroup and that the dialout group has rw access. If not you will need to set\n it.
          4. \n
          5. Users also need to be members of the i2c group. If it does not exist create \n it and then make that the group for /dev/i2c-1 with group rw permissions. \nTHIS MAY NOT BE NECESSARY.
          6. \n
          7. The spi group needs to be created (addgroup?).
          8. \n
          9. Additionally the spi group needs to be given rw access to the spi devices\nat each boot. To do this create a one line rule in a file named \n/etc/udev/rules.d/50-spidev.rules containing SUBSYSTEM==\"spidev\", \nGROUP=\"spi\", MODE=\"0660\". The file should have rw permission for root \nand read permission for everyone else.
          10. \n
          11. Make sure you have pip installed for \npython 3: python3 -m pip --version or pip3 --version. If you do not, \ninstall using apt \ninstall python3-pip.
          12. \n
          \n\n

          Raspberrian on Pi

          \n\n

          (TBD)

          \n\n

          Non-Pi based System Initial Setup

          \n\n

          Make sure that Python >=3.6 is installed: python3 -v. If not follow \ninstructions at python.org. This software should run \non any computer capable of supporting the necessary version of Python. \nHowevever, it will only run in demo mode if the computer does not support \none of the compatible A-to-D boards.

          \n\n

          Generic Linux

          \n\n
            \n
          • If your system hardware \nhas GPIO pins and a GPIO interface board, you should try following the \ninstructions for a Pi based system above. If \nyou figure out how to make this work on other SBCs or systems with GPIO, \nplease submit a pull request updating these instructions.
          • \n
          • If your system hardware does not support GPIO and one of the compatible \ninterface boards, the software will run in demo mode.
          • \n
          \n\n

          NOTE: If a binary distribution (whl or wheel) is not available for your\nplatform, some of the required packages may need to be compiled. If you get\ncompilation errors when installing try getting the python header and \ndevelopment files for your platform. To get them on most *nix platforms use the\ncommand sudo apt install python3-dev.

          \n\n

          MacOS

          \n\n

          Currently, the LabQuest A-to-Ds are only working on intel based macs. When \nVernier recompiles their interface communication library for the M1 series \ncpus this will change.

          \n\n
            \n
          1. Install the latest version of Python, by following the instructions on \nthe Python website.
          2. \n
          3. Make sure a virtual environment tool such as pipenv is installed. In a \nterminal window try python -m pip install --user pipenv.
          4. \n
          \n\n

          Windows

          \n\n

          Note: LoggerPro software may need to be \ninstalled because that provides the compiled .dll necessary to communicate \nwith the LabQuest interface. In theory will work without this.

          \n\n
            \n
          1. Install the latest version of Python, by following the instructions on \nthe Python website. WARNING: Do Not use \nWindows automatic installation. It may work on a plain vanilla system \nwith one user, but in multiuser systems it badly messes up the ability to \nuse virtual environments.
          2. \n
          3. Make sure a virtual environment tool such as pipenv is installed. In a \nterminal window try python -m pip install --user pipenv.
          4. \n
          5. You may have to set or add to your user PATH environment variable. See \nthe warnings generated during the installation. Then search for the \nlatest instructions on how to set PATH.
          6. \n
          \n\n

          Final Set Up

          \n\n
            \n
          • If on Raspberry Pi type system, make sure the user you will be running the \nsoftware under is a member of the groups dialout, spi and if it \nexistsi2c.
          • \n
          • It is recommended that you install JupyterPiDAQ in its own \nvirtual environment.\nThe instructions below do just that using the original author's favorite \nvirtual environment tool pipenv.
          • \n
          \n\n

          Log into your chosen user account:

          \n\n
            \n
          1. Install pipenv: pip3 install \n--user pipenv. You may\nneed to add ~/.local/bin to your PATH to make pipenv\navailable in your command shell. More discussion: \nThe Hitchhiker's Guide to\nPython.
          2. \n
          3. Create a directory for the virtual environment you will be installing\ninto (example: mkdir JupyterPiDAQ).
          4. \n
          5. Navigate into the directory cd JupyterPiDAQ.
          6. \n
          7. Create the virtual environment and enter it pipenv shell. To get out of\nthe environment you can issue the exit command on the command line.
          8. \n
          9. While still in the shell install the latest JupyterPiDAQ and all its\nrequirements\n pip install -U JupyterPiDAQ. This can take a long time, especially on a\n Raspberry Pi. On a Pi 3B+ (minimum requirement) it will probably not run\n without at least 1 GB of swap. See: Build Jupyter on a Pi\n for a discussion of adding swap space on a Pi.
          10. \n
          11. Still within the environment shell test\nthis by starting jupyter jupyter notebook or jupyter lab. Jupyter should \nlaunch in your browser.\n
              \n
            1. Open a new notebook using the default (Python 3) kernel.
            2. \n
            3. In the first cell import all from DAQinstance.py: \nfrom jupyterpidaq.DAQinstance import *.\n When run in jupyter notebook this cell should load the DAQmenu at the \n end of the Jupyter notebook menu/icon bar. Currently, no convenience \n menu is available in Jupyter lab. If you do not have an appropriate\n A-to-D board installed you will get a message and the software\n will default to demo mode, substituting a random number\n generator for the A-to-D.
            4. \n
          12. \n
          13. If you wish, you can make this environment available to an alternate Jupyter\ninstall as a special kernel when you are the user.\n
              \n
            1. Make sure you are running in your virtual environment pipenv shell \nin the directory for virtual environment will do that.
            2. \n
            3. Issue the command to add this as a kernel to your personal space: \npython -m ipykernel install --user --name=<name-you-want-for-kernel>.
            4. \n
            5. More information is available in the Jupyter/IPython documentation. \nA simple tutorial from Nikolai Jankiev (_Parametric Thoughts_) can be\nfound here.
            6. \n
          14. \n
          \n\n

          Development Notes

          \n\n

          Code Repository

          \n\n

          Setting up Development Environment

          \n\n

          Basic requirements: Python 3.6+, associated\npip and a Jupyter notebook.\nSee: python.org and\nJupyter.org.

          \n\n
            \n
          1. If not installed, install pipenv:pip3 install --user pipenv. You may\nneed to add ~/.local/bin to your PATH to make pipenv\navailable in your command shell. More discussion: \nThe Hitchhiker's Guide to Python.
          2. \n
          3. Navigate to the directory where this package will be\nor has been downloaded to. Use pipenvto install an \n\"editable\" package \ninside the directory as described below:\n
              \n
            1. Start a shell in the environment pipenv shell.
            2. \n
            3. Install using pip.\n
                \n
              1. If you downloaded the git repository named \"JupyterPiDAQ\"\nand have used that directory to build your virtual\nenvironment: pip install -e ../JupyterPiDAQ/.
              2. \n
              3. If you are downloading from PyPi\npip install -e JupyterPiDAQ
              4. \n
              5. Either should install all the additional packages this\npackage depends upon. On a Raspberry Pi this will take\na long time. It probably will not run without at least 1 GB of swap. See: \nBuild Jupyter on a Pi\n.
              6. \n
            4. \n
            5. Still within the environment shell test this by starting jupyter \njupyter nbclassic or jupyter lab. Jupyter should launch in your \nbrowser.\n
                \n
              1. Open a new notebook using the default (Python 3) kernel.
              2. \n
              3. In the first cell import all from DAQinstance.py: \nfrom jupyterpidaq.DAQinstance import *.\nWhen run this cell should load the DAQmenu at the end of the\nJupyter notebook menu/icon bar. If you do not have an appropriate A-to-D\nboard installed you will get a message and the software\nwill default to demo mode, substituting a random number\ngenerator for the A-to-D. Because of the demo mode it is\npossible to run this on any computer, not just a Pi.
              4. \n
            6. \n
          4. \n
          5. If you wish, you can make this environment available to an alternate Jupyter\ninstall as a special kernel when you are the user.\n
              \n
            1. Make sure you are running in your virtual environment pipenv shell \nin the directory for virtual environment will do that.
            2. \n
            3. Issue the command to add this as a kernel to your personal space: \npython -m ipykernel install --user --name=<name-you-want-for-kernel>.
            4. \n
            5. More information is available in the Jupyter/Ipython documentation. \nA simple tutorial from Nikolai Jankiev (_Parametric Thoughts_) can be\nfound here.
            6. \n
          6. \n
          \n\n

          Adding New Sensor Code

          \n\n
            \n
          1. Copy an existing sensor class paste it into the end of\nsensors.py and rename it.
          2. \n
          3. Update/delete functions for each valid unit within the new\nclass as necessary.
          4. \n
          5. Update the sensor name, vendor and available units in the\n__init__ function.
          6. \n
          7. Add the new sensor classname to the list of available sensors\nin listSensors at about line 120 of sensors.py.
          8. \n
          9. Add the new sensor classname to getsensors of ADCsim.py,\nADCsim_line.py and any board (e.g. DAQC2.py) with which the sensor\ncan be used. Do not guess if a sensor works with a particular\nboard. Test it!
          10. \n
          \n\n

          Running Tests

          \n\n
            \n
          1. Install updated pytest in the virtual environment:\n
            pipenv shell\npip install -U pytest\n
          2. \n
          3. Run tests ignoring the manual tests in the dev_testing directory:\npython -m pytest --ignore='dev_testing'.
          4. \n
          \n\n

          Building Documentation

          \n\n
            \n
          1. Install or update pdoc into the virtual environment pip install -U pdoc.
          2. \n
          3. Make edits to the .md files within the docs folder that are to be \nincluded in the first page (see __init__.py of the jupyterpidaq package).
          4. \n
          5. At the root level run pdoc \n--logo https://jupyterphysscilab.github.io/JupyterPiDAQ/JupyterPiDAQ-logo.svg --logo-link \nhttps://jupyterphysscilab.github.io/JupyterPiDAQ/ --footer-text \n\"JupyterPiDAQ vX.X.X\" -html -o docs jupyterpidaq Unless you are on a \nRaspbery Pi this will throw an error about import. Just ignore.
          6. \n
          \n\n

          Building PyPi package

          \n\n
            \n
          1. Make sure to update the version number in setup.py first.
          2. \n
          3. Install updated setuptools and twine in the virtual environment:\n
            pipenv shell\npip install -U setuptools wheel twine\n
          4. \n
          5. Build the distribution python -m setup sdist bdist_wheel.
          6. \n
          7. Test it on test.pypi.org.\n
              \n
            1. Upload it (you will need an account on test.pypi.org):\npython -m twine upload --repository testpypi dist/*.
            2. \n
            3. Create a new virtual environment and test install into it:\n
              exit # to get out of the current environment\ncd <somewhere>\nmkdir <new virtual environment>\ncd <new directory>\npipenv shell #creates the new environment and enters it.\npip install -i https://test.pypi.org/..... # copy actual link from the\n                                           # repository on test.pypi.\n
              \nThere are often install issues because sometimes only older versions of\nsome of the required packages are available on test.pypi.org. If this\nis the only problem change the version to end in rc0 for release\ncandidate and try it on the regular pypi.org as described below for\nreleasing on PyPi.
            4. \n
            5. After install test by running a jupyter notebook in the virtual \nenvironment.
            6. \n
          8. \n
          \n\n

          Releasing on PyPi

          \n\n

          Proceed only if testing of the build is successful.

          \n\n
            \n
          1. Double check the version number in setup.py.
          2. \n
          3. Rebuild the release: python -m setup sdist bdist_wheel.
          4. \n
          5. Upload it: python -m twine upload dist/*
          6. \n
          7. Make sure it works by installing it in a clean virtual environment. This\nis the same as on test.pypi.org except without -i https://test.pypy.... If\nit does not work, pull the release.
          8. \n
          \n\n

          Change Log

          \n\n
            \n
          • 0.8.1 (May 2, 2023)\n
              \n
            • BUG FIX: Now always waits for all data to be transferred to plot before \nwriting backup to a file.
            • \n
            • More rapid update of display when using LabQuests.
            • \n
            • Docs now recommend launching jupyter nbclassic for old notebook interface.
            • \n
          • \n
          • 0.8.0 (Apr. 20, 2023)\n
              \n
            • Replaced NewRun() command with Run() command. This version works in \nJupyter Lab and removes the need for the DisplayRun() command because \nRun() will load an already collected dataset or start a new one.
            • \n
            • Now, when multiple traces are assigned to the same channel of a board the \nchannel is only read one time. If the units are different the two \ntraces will be displayed with different units.
            • \n
            • Can read data from Vernier LabQuest USB \nanalog-to-digital interfaces. Potential data rate is 10 kHz, currently \nlimited to 20 Hz.
            • \n
            • Runs in Jupyter Lab (no menus yet) and Notebook \n(menus still work).
            • \n
            • Updated requirements to include jupyterlab and labquest packages.
            • \n
          • \n
          • 0.7.9 (Mar. 9 2023)\n
              \n
            • Added spidev package to requirements because pi-plates requires it.
            • \n
            • More robust exception handling when searching for boards/A-to-Ds.
            • \n
          • \n
          • 0.7.8\n
              \n
            • Updated text for insertion into cells to make better use of escaping \nupdates in JPSLUtils >=0.7.0.
            • \n
            • Removed some unnecessary print statements.
            • \n
          • \n
          • 0.7.7\n
              \n
            • Updated requirements for upstream security fixes.
            • \n
            • Conversion to pandas dataframe now works when trace 0 is not collected.
            • \n
            • DAQ menu no longer created in trusted notebooks if the data acquisition \ntools have not been initialized since the notebook was opened.
            • \n
            • Reworked the data collection so that opening an old notebook without \nrunning anything will not have any leftover inoperable or undefined \nwidgets.
            • \n
            • Reordered the live trace display to match the order of the names at right.
            • \n
            • Runs now saved to a human readable html file that includes the run \nconditions.
            • \n
            • As long as this html file is in the same directory as the notebook, the \nrun display can be recreated by running its cell after accidentally \nclearing the cell output.
            • \n
            • The cell displaying the results of a run is now protected against \ndeletion and editing.
            • \n
          • \n
          • 0.7.6\n
              \n
            • Converted to fancy menus (could make hierarchical).
            • \n
          • \n
          • 0.7.5\n
              \n
            • Added fitting to DAQ command menu.
            • \n
            • Documentation Enhancements: github.io website; first pass as API docs; \nreorganized documentation; MyBinder link now forces launch in classic \nnotebook; added plans for adapter board to connect Vernier Sensors.
            • \n
          • \n
          • 0.7.4.1\n
              \n
            • Improved layout of data collection.
            • \n
            • Better widget cleanup.
            • \n
            • Readme fixes.
            • \n
          • \n
          • 0.7.3 Pip install reliability fixes.
          • \n
          • 0.7.2 Suppress Javascript error when not in JLab.
          • \n
          • 0.7.1\n
              \n
            • Include Heat Capacity Lab example.
            • \n
            • Make menu show up in JLab (still not functional).
            • \n
            • Remove matplotlib baggage.
            • \n
          • \n
          • 0.7.0\n
              \n
            • Switched to plotly widget for plotting.
            • \n
            • Added Vernier pressure sensor calibrations (old and new).
            • \n
            • Jupyter widgets based new calculated column GUI.
            • \n
            • Jupyter widgets based new plot GUI.
            • \n
            • Default to providing only one time for channels collected nearly \nsimultaneously.
            • \n
            • As reported values are averages, switched to reporting the estimated \nstandard deviation of the average rather than the deviation of all the \nreadings used to create the average.
            • \n
          • \n
          • 0.6.0 \n
              \n
            • Initial release.
            • \n
            • Live data collection.
            • \n
            • Recognized sensors: ADS1115 boards (voltage, built-in thermistor, \nVernier SS temperature probe), DAQC2 boards (voltage,Vernier SS \ntemperature probe, Vernier standard pH probe, Vernier flat pH probe).
            • \n
          • \n
          \n\n

          Three Channel Adapter Board for Vernier Sensors

          \n\n

          Board plans

          \n\n

          \"board

          \n\n

          Etching the board

          \n\n

          Clean Cu cladding to be etched by wet sanding with 1500 grit or finer.\nDraw traces with a black sharpie \u201cindustrial super permanent ink\u201d version\nworks best. Allow to dry completely (no odor). Can be speeded up using a heat\ngun.

          \n\n

          Etchant recipe:

          \n\n

          3 M HCl

          \n\n

          CuCl2 to make medium emerald green solution (can also be made by \ndissolving Cu after adding hydrogen peroxide)

          \n\n

          Using test piece add 30% H2O2 dropwise to get gentle \nbubbling and \ncomplete\nremoval in 3 \u2013 5 min (longer gives poorly defined edges).

          \n"}, {"fullname": "jupyterpidaq.Boards", "modulename": "jupyterpidaq.Boards", "kind": "module", "doc": "

          This module wraps all the submodules related to communication and control of\ndata acquisition boards.

          \n"}, {"fullname": "jupyterpidaq.Boards.PiGPIO", "modulename": "jupyterpidaq.Boards.PiGPIO", "kind": "module", "doc": "

          This module contains the modules for all PiGPIO based boards.

          \n"}, {"fullname": "jupyterpidaq.Boards.PiGPIO.ADS1115", "modulename": "jupyterpidaq.Boards.PiGPIO.ADS1115", "kind": "module", "doc": "

          \n"}, {"fullname": "jupyterpidaq.Boards.PiGPIO.ADS1115.find_boards", "modulename": "jupyterpidaq.Boards.PiGPIO.ADS1115", "qualname": "find_boards", "kind": "function", "doc": "

          A routine like this must be implemented by all board packages.

          \n\n
          Returns
          \n\n
          \n

          list of ADS1115 board objects (maximum of 4 boards)

          \n
          \n", "signature": "():", "funcdef": "def"}, {"fullname": "jupyterpidaq.Boards.PiGPIO.ADS1115.Board_ADS1115", "modulename": "jupyterpidaq.Boards.PiGPIO.ADS1115", "qualname": "Board_ADS1115", "kind": "class", "doc": "

          Class defining the properties of Adafruit compatible ADS1115\nanalog-to-digital boards for Raspberry-Pi style GPIO. Key characteristics:

          \n\n
            \n
          • 4 channels (0 - 3) with 16 bit resolution and a range of +/- 3.3 V
          • \n
          • Programmable gain on each channel of 2/3, 1, 2, 4, 8, 16 making these\ngood for small signals.
          • \n
          • a differential mode is available but not implemented in this class.
          • \n
          \n", "bases": "jupyterpidaq.Boards.boards.Board"}, {"fullname": "jupyterpidaq.Boards.PiGPIO.ADS1115.Board_ADS1115.__init__", "modulename": "jupyterpidaq.Boards.PiGPIO.ADS1115", "qualname": "Board_ADS1115.__init__", "kind": "function", "doc": "

          Should be overridden by each board and define at minimum:\nself.name = 'board name/adc name/type' Short an useful to end user\nself.vendor = 'Vendor/Manufacturer name`\nself.channels = tuple of available channel IDs\nself.gains = list of gains\nself.Vdd = voltage provided by board to sensors

          \n", "signature": "(adc)"}, {"fullname": "jupyterpidaq.Boards.PiGPIO.ADS1115.Board_ADS1115.getsensors", "modulename": "jupyterpidaq.Boards.PiGPIO.ADS1115", "qualname": "Board_ADS1115.getsensors", "kind": "function", "doc": "

          Return a list of valid sensor object names for this board.

          \n\n
          Returns
          \n\n
          \n

          list of classnames

          \n
          \n", "signature": "(self):", "funcdef": "def"}, {"fullname": "jupyterpidaq.Boards.PiGPIO.ADS1115.Board_ADS1115.V_oversampchan", "modulename": "jupyterpidaq.Boards.PiGPIO.ADS1115", "qualname": "Board_ADS1115.V_oversampchan", "kind": "function", "doc": "

          This routine returns the average voltage for the channel\naveraged at (0.0012 + 1/data_rate)^-1 Hz for avg_sec\nnumber of seconds. The 0.0012 is the required loop time\non a RPI 3B+ in python.

          \n\n

          Returns a tuple of the following 5 objects:\n V_avg -- float, the averaged voltage

          \n\n
          V_min -- float, the minimum voltage read during\nthe interval\n\nV_max -- float, the maximum voltage read during the\ninterval\n\ntime_stamp -- float, the time at halfway through the averaging\ninterval in seconds since the beginning of the epoch (OS\ndependent begin time)\n\nself.Vdd -- float, the reference voltage.\n
          \n\n
          Parameters
          \n\n
            \n
          • int chan: the channel number (0, 1, 2, 3)

          • \n
          • gain: 2/3 (+/-6.144V), 1(+/-4.096V), 2(+/-2.048V),\n4(+/-1.024V), 8 (+/-0.512V), 16 (+/-0.256V))

          • \n
          • int data_rate: the ADC sample rate in Hz (8, 16, 32, 64,\n128,250, 475 or 860 Hz). Set to 475 Hz by default.

          • \n
          • float avg_sec: seconds to average for, actual\naveraging interval will be as close as possible for an integer\nnumber of samples

          • \n
          \n\n

          :returns: V_avg, V_min, V_max, time_stamp, self.Vdd

          \n\n
          Returns
          \n\n
          \n

          description

          \n
          \n\n
          Returns
          \n\n
          \n

          \n
          \n\n
          Returns
          \n\n
          \n

          \n
          \n\n
          Returns
          \n\n
          \n

          \n
          \n\n
          Returns
          \n", "signature": "(self, chan, gain, avg_sec, data_rate=475):", "funcdef": "def"}, {"fullname": "jupyterpidaq.Boards.PiGPIO.ADS1115.Board_ADS1115.V_oversampchan_stats", "modulename": "jupyterpidaq.Boards.PiGPIO.ADS1115", "qualname": "Board_ADS1115.V_oversampchan_stats", "kind": "function", "doc": "

          This routine returns the average voltage for the channel\naveraged at (0.0012 + 1/data_rate)^-1 Hz for avg_sec\nnumber of seconds. The 0.0012 is the required loop time\non a RPI 3B+ in python3. The standard\ndeviation and the estimated deviation of the mean are also\nreturned.

          \n\n

          Returns a tuple of the following 5 objects:\n V_avg -- float, the averaged voltage

          \n\n
          stdev -- float, the standard deviation of the measured values\nduring the averaging interval\n\nstdev_avg -- float, the estimated standard deviation of the\nreturned average\n\ntime_stamp -- float, the time at halfway through the averaging\ninterval in seconds since the beginning of the epoch (OS\ndependent begin time)\n\nself.Vdd -- float, the reference voltage.\n
          \n\n
          Parameters
          \n\n
            \n
          • int chan: the channel number (0, 1, 2, 3)

          • \n
          • gain: 2/3 (+/-6.144V), 1(+/-4.096V), 2(+/-2.048V),\n4(+/-1.024V), 8 (+/-0.512V), 16 (+/-0.256V))

          • \n
          • int data_rate: the ADC sample rate in Hz (8, 16, 32, 64,\n128,250, 475 or 860 Hz). Set to 475 Hz by default.

          • \n
          • float avg_sec: seconds to average for, actual\naveraging interval will be as close as possible for an integer\nnumber of samples

          • \n
          \n\n

          :returns: VV_avg, stdev, stdev_avg, time_stamp, self.Vdd

          \n\n
          Returns
          \n\n
          \n

          description

          \n
          \n\n
          Returns
          \n\n
          \n

          \n
          \n\n
          Returns
          \n\n
          \n

          \n
          \n\n
          Returns
          \n\n
          \n

          \n
          \n\n
          Returns
          \n", "signature": "(self, chan, gain, avg_sec, data_rate=475):", "funcdef": "def"}, {"fullname": "jupyterpidaq.Boards.PiGPIO.ADS1115.Board_ADS1115.V_sampchan", "modulename": "jupyterpidaq.Boards.PiGPIO.ADS1115", "qualname": "Board_ADS1115.V_sampchan", "kind": "function", "doc": "

          This routine returns the voltage for the

          \n\n

          Returns a tuple of the following 3 objects:\n V -- float, the measured voltage

          \n\n
          time_stamp -- float, the time at halfway through the averaging\ninterval in seconds since the beginning of the epoch (OS\ndependent begin time)\n\nself.Vdd -- float, the reference voltage.\n
          \n\n
          Parameters
          \n\n
            \n
          • int chan: the channel number (0, 1, 2, 3)

          • \n
          • gain: 2/3 (+/-6.144V), 1(+/-4.096V), 2(+/-2.048V),\n4(+/-1.024V), 8 (+/-0.512V), 16 (+/-0.256V))

          • \n
          • int data_rate: the ADC sample rate in Hz (8, 16, 32, 64,\n128,250, 475 or 860 Hz). Set to 475 Hz by default.

          • \n
          \n\n

          :returns: V, time_stamp, self.Vdd

          \n\n
          Returns
          \n\n
          \n

          \n
          \n\n
          Returns
          \n\n
          \n

          \n
          \n\n
          Returns
          \n", "signature": "(self, chan, gain, data_rate=475):", "funcdef": "def"}, {"fullname": "jupyterpidaq.Boards.PiGPIO.DAQC2", "modulename": "jupyterpidaq.Boards.PiGPIO.DAQC2", "kind": "module", "doc": "

          \n"}, {"fullname": "jupyterpidaq.Boards.PiGPIO.DAQC2.find_boards", "modulename": "jupyterpidaq.Boards.PiGPIO.DAQC2", "qualname": "find_boards", "kind": "function", "doc": "

          A rountine like this must be implemented by all board packages.

          \n\n
          Returns
          \n\n
          \n

          list of DAQC2 board objects (maximum of 8 boards)

          \n
          \n", "signature": "():", "funcdef": "def"}, {"fullname": "jupyterpidaq.Boards.PiGPIO.DAQC2.Board_DAQC2", "modulename": "jupyterpidaq.Boards.PiGPIO.DAQC2", "qualname": "Board_DAQC2", "kind": "class", "doc": "

          Class defining the properties of the analog-to-digital block of the\npi-Plates DAQC2 board. Key characteristics:

          \n\n
            \n
          • 8 channels (0 - 7) with pseudo 16 bit resolution (oversampled 14 bit) and\na range of +/- 12 V.
          • \n
          • 1 channel (8) dedicated to monitoring Vdd.
          • \n
          • Programmable RGB LED to use as indicator.
          • \n
          • Other available facilities are Digital I/O, Digital-to-Analog and\n2-channel o-scope modes. These are not supported by this class.
          • \n
          \n", "bases": "jupyterpidaq.Boards.boards.Board"}, {"fullname": "jupyterpidaq.Boards.PiGPIO.DAQC2.Board_DAQC2.__init__", "modulename": "jupyterpidaq.Boards.PiGPIO.DAQC2", "qualname": "Board_DAQC2.__init__", "kind": "function", "doc": "

          Should be overridden by each board and define at minimum:\nself.name = 'board name/adc name/type' Short an useful to end user\nself.vendor = 'Vendor/Manufacturer name`\nself.channels = tuple of available channel IDs\nself.gains = list of gains\nself.Vdd = voltage provided by board to sensors

          \n", "signature": "(addr)"}, {"fullname": "jupyterpidaq.Boards.PiGPIO.DAQC2.Board_DAQC2.getsensors", "modulename": "jupyterpidaq.Boards.PiGPIO.DAQC2", "qualname": "Board_DAQC2.getsensors", "kind": "function", "doc": "

          Return a list of valid sensor object names for this board.

          \n\n
          Returns
          \n\n
          \n

          list of classnames

          \n
          \n", "signature": "(self):", "funcdef": "def"}, {"fullname": "jupyterpidaq.Boards.PiGPIO.DAQC2.Board_DAQC2.V_oversampchan", "modulename": "jupyterpidaq.Boards.PiGPIO.DAQC2", "qualname": "Board_DAQC2.V_oversampchan", "kind": "function", "doc": "

          This routine returns the average voltage for the channel\naveraged at the default rate for the board and returns an\naverage and observed range.

          \n\n

          Returns a tuple of the following 5 objects:\n V_avg -- float, the averaged voltage

          \n\n
          V_min -- float, the minimum voltage read during\nthe interval\n\nV_max -- float, the maximum voltage read during the\ninterval\n\ntime_stamp -- float, the time at halfway through the averaging\ninterval in seconds since the beginning of the epoch (OS\ndependent begin time)\n\nVdd_avg -- float, the reference voltage (Vdd) collected\nsimultaneously.\n
          \n\n
          Parameters
          \n\n
            \n
          • int chan: the channel number (0, 1, 2, 3, 4, 5, 6, 7,\n8). NOTE: channel 8 returns a measurement of Vdd.

          • \n
          • gain: ignored by board. Defaults to 1.

          • \n
          • int data_rate: ignored by board.

          • \n
          • float avg_sec: seconds to average for, actual\naveraging interval will be as close as possible for an integer\nnumber of samples

          • \n
          \n\n

          :returns: V_avg, V_min, V_max, time_stamp, Vdd_avg

          \n\n
          Returns
          \n\n
          \n

          description

          \n
          \n\n
          Returns
          \n\n
          \n

          \n
          \n\n
          Returns
          \n\n
          \n

          \n
          \n\n
          Returns
          \n\n
          \n

          \n
          \n\n
          Returns
          \n", "signature": "(self, chan, gain, avg_sec, data_rate=475):", "funcdef": "def"}, {"fullname": "jupyterpidaq.Boards.PiGPIO.DAQC2.Board_DAQC2.V_oversampchan_stats", "modulename": "jupyterpidaq.Boards.PiGPIO.DAQC2", "qualname": "Board_DAQC2.V_oversampchan_stats", "kind": "function", "doc": "

          This routine returns the average voltage for the channel\naveraged at the maximum rate for the board. The standard\ndeviation and the estimated deviation of the mean are also\nreturned.

          \n\n

          Returns a tuple of the following 5 objects:\n V_avg -- float, the averaged voltage

          \n\n
          stdev -- float, the standard deviation of the measured values\nduring the averaging interval\n\nstdev_avg -- float, the estimated standard deviation of the\nreturned average\n\ntime_stamp -- float, the time at halfway through the averaging\ninterval in seconds since the beginning of the epoch (OS\ndependent begin time)\n\nVdd_avg -- float, the reference voltage (Vdd) collected\nsimultaneously.\n
          \n\n
          Parameters
          \n\n
            \n
          • int chan: the channel number (0, 1, 2, 3, 4, 5, 6, 7,\n8). NOTE: channel 8 returns a measurement of Vdd.

          • \n
          • gain: ignored by board. Defaults to 1.

          • \n
          • int data_rate: ignored by board.

          • \n
          • float avg_sec: seconds to average for, actual\naveraging interval will be as close as possible for an integer\nnumber of samples

          • \n
          \n\n

          :returns: V_avg, stdev, stdev_avg, time_stamp, Vdd_avg

          \n\n
          Returns
          \n\n
          \n

          description

          \n
          \n\n
          Returns
          \n\n
          \n

          \n
          \n\n
          Returns
          \n\n
          \n

          \n
          \n\n
          Returns
          \n\n
          \n

          \n
          \n\n
          Returns
          \n", "signature": "(self, chan, gain, avg_sec, data_rate=475):", "funcdef": "def"}, {"fullname": "jupyterpidaq.Boards.PiGPIO.DAQC2.Board_DAQC2.V_sampchan", "modulename": "jupyterpidaq.Boards.PiGPIO.DAQC2", "qualname": "Board_DAQC2.V_sampchan", "kind": "function", "doc": "

          This routine returns a single reading of the voltage for the channel.

          \n\n

          Returns a tuple of the following 5 objects:\n V -- float, the measured voltage

          \n\n
          time_stamp -- float, the time of the measurement in seconds since\nthe beginning of the epoch (OS dependent begin time)\n\nref -- float, the reference voltage (Vdd) collected\nsimultaneously.\n
          \n\n
          Parameters
          \n\n
            \n
          • int chan: the channel number (0, 1, 2, 3, 4, 5, 6, 7,\n8). NOTE: channel 8 returns a measurement of Vdd.

          • \n
          • gain: ignored by board. Defaults to 1.

          • \n
          • int data_rate: ignored by board.

          • \n
          \n\n

          :returns: V_avg, stdev, stdev_avg, time_stamp, Vdd_avg

          \n\n
          Returns
          \n\n
          \n

          \n
          \n\n
          Returns
          \n\n
          \n

          \n
          \n\n
          Returns
          \n", "signature": "(self, chan, gain, data_rate=475):", "funcdef": "def"}, {"fullname": "jupyterpidaq.Boards.Simulated", "modulename": "jupyterpidaq.Boards.Simulated", "kind": "module", "doc": "

          This module wraps modules for simulated boards.

          \n"}, {"fullname": "jupyterpidaq.Boards.Simulated.ADCsim", "modulename": "jupyterpidaq.Boards.Simulated.ADCsim", "kind": "module", "doc": "

          \n"}, {"fullname": "jupyterpidaq.Boards.Simulated.ADCsim.find_boards", "modulename": "jupyterpidaq.Boards.Simulated.ADCsim", "qualname": "find_boards", "kind": "function", "doc": "

          \n", "signature": "():", "funcdef": "def"}, {"fullname": "jupyterpidaq.Boards.Simulated.ADCsim.Board_ADCsim_random", "modulename": "jupyterpidaq.Boards.Simulated.ADCsim", "qualname": "Board_ADCsim_random", "kind": "class", "doc": "

          Base class for all boards. Each board should be an extension of this class.

          \n", "bases": "jupyterpidaq.Boards.boards.Board"}, {"fullname": "jupyterpidaq.Boards.Simulated.ADCsim.Board_ADCsim_random.__init__", "modulename": "jupyterpidaq.Boards.Simulated.ADCsim", "qualname": "Board_ADCsim_random.__init__", "kind": "function", "doc": "

          Should be overridden by each board and define at minimum:\nself.name = 'board name/adc name/type' Short an useful to end user\nself.vendor = 'Vendor/Manufacturer name`\nself.channels = tuple of available channel IDs\nself.gains = list of gains\nself.Vdd = voltage provided by board to sensors

          \n", "signature": "(adc)"}, {"fullname": "jupyterpidaq.Boards.Simulated.ADCsim.Board_ADCsim_random.getsensors", "modulename": "jupyterpidaq.Boards.Simulated.ADCsim", "qualname": "Board_ADCsim_random.getsensors", "kind": "function", "doc": "

          Return a list of valid sensor object names for this board.

          \n\n
          Returns
          \n\n
          \n

          list of classnames

          \n
          \n", "signature": "(self):", "funcdef": "def"}, {"fullname": "jupyterpidaq.Boards.Simulated.ADCsim.Board_ADCsim_random.V_oversampchan_stats", "modulename": "jupyterpidaq.Boards.Simulated.ADCsim", "qualname": "Board_ADCsim_random.V_oversampchan_stats", "kind": "function", "doc": "

          This routine returns the average voltage for the channel\naveraged at (0.0012 + 1/data_rate)^-1 Hz for avg_sec\nnumber of seconds. The 0.0012 is the required loop time\non a RPI 3B+ in python3. The voltage is rounded to the number\nof decimals indicated by the standard deviation. The standard\ndeviation and the estimated deviation of the mean are also\nreturned.\nParameters\n chan the channel number 0, 1, 2, 3\n gain 2/3 (+/-6.144V), 1(+/-4.096V), 2(+/-2.048V), 4(+/-1.024V),\n 8 (+/-0.512V), 16 (+/-0.256V)\n data_rate the ADC sample rate in Hz (8, 16, 32, 64, 128, 250, 475 or 860 Hz)\n avg_sec seconds to average for, actual averaging interval will be as close\n as possible for an integer number of samples.\nReturns a tuple (V_avg, V_min, V_max, time_stamp)\n V_avg the averaged voltage\n stdev estimated standard deviation of the measurements\n stdev_avg estimated standard deviation of the mean\n time_stamp the time at halfway through the averaging interval in seconds\n since the beginning of the epoch (OS dependent begin time).

          \n", "signature": "(self, chan, gain, avg_sec, data_rate=475):", "funcdef": "def"}, {"fullname": "jupyterpidaq.Boards.Simulated.ADCsim.Board_ADCsim_random.V_oversampchan", "modulename": "jupyterpidaq.Boards.Simulated.ADCsim", "qualname": "Board_ADCsim_random.V_oversampchan", "kind": "function", "doc": "

          This routine returns the average voltage for the channel\naveraged at (0.0012 + 1/data_rate)^-1 Hz for avg_sec\nnumber of seconds. The 0.0012 is the required loop time\non a RPI 3B+ in python3. The voltage is rounded to the number\nof decimals indicated by the standard deviation. The standard\ndeviation and the estimated deviation of the mean are also\nreturned.\nParameters\n chan the channel number 0, 1, 2, 3\n gain 2/3 (+/-6.144V), 1(+/-4.096V), 2(+/-2.048V), 4(+/-1.024V),\n 8 (+/-0.512V), 16 (+/-0.256V)\n data_rate the ADC sample rate in Hz (8, 16, 32, 64, 128, 250, 475 or 860 Hz)\n avg_sec seconds to average for, actual averaging interval will be as close\n as possible for an integer number of samples.\nReturns a tuple (V_avg, V_min, V_max, time_stamp)\n V_avg the averaged voltage\n stdev estimated standard deviation of the measurements\n stdev_avg estimated standard deviation of the mean\n time_stamp the time at halfway through the averaging interval in seconds\n since the beginning of the epoch (OS dependent begin time).

          \n", "signature": "(self, chan, gain, avg_sec, data_rate=475):", "funcdef": "def"}, {"fullname": "jupyterpidaq.Boards.Simulated.ADCsim.Board_ADCsim_random.V_sampchan", "modulename": "jupyterpidaq.Boards.Simulated.ADCsim", "qualname": "Board_ADCsim_random.V_sampchan", "kind": "function", "doc": "

          This function returns a single measurement and the time it was\ncollected.

          \n\n
          Parameters
          \n\n
            \n
          • chan: id of the channel to be measured
          • \n
          • gain: gain of the channel if adjustable
          • \n
          \n\n
          Returns
          \n\n
          \n

          a tuple consisting of V, time_stamp, where V = the single\n voltage measurement and time_stamp the time it was collected.

          \n
          \n", "signature": "(self, chan, gain, **kwargs):", "funcdef": "def"}, {"fullname": "jupyterpidaq.Boards.Simulated.ADCsim_line", "modulename": "jupyterpidaq.Boards.Simulated.ADCsim_line", "kind": "module", "doc": "

          \n"}, {"fullname": "jupyterpidaq.Boards.Simulated.ADCsim_line.find_boards", "modulename": "jupyterpidaq.Boards.Simulated.ADCsim_line", "qualname": "find_boards", "kind": "function", "doc": "

          \n", "signature": "():", "funcdef": "def"}, {"fullname": "jupyterpidaq.Boards.Simulated.ADCsim_line.Board_ADCsim_line", "modulename": "jupyterpidaq.Boards.Simulated.ADCsim_line", "qualname": "Board_ADCsim_line", "kind": "class", "doc": "

          This class simulates an Analog-to-Digital board that returns a linearly\nincreasing signal with a small amount of noise on the signal. The\nintercept and slope depend upon which hour of the day the simulation is\nrun.

          \n", "bases": "jupyterpidaq.Boards.boards.Board"}, {"fullname": "jupyterpidaq.Boards.Simulated.ADCsim_line.Board_ADCsim_line.__init__", "modulename": "jupyterpidaq.Boards.Simulated.ADCsim_line", "qualname": "Board_ADCsim_line.__init__", "kind": "function", "doc": "

          Should be overridden by each board and define at minimum:\nself.name = 'board name/adc name/type' Short an useful to end user\nself.vendor = 'Vendor/Manufacturer name`\nself.channels = tuple of available channel IDs\nself.gains = list of gains\nself.Vdd = voltage provided by board to sensors

          \n", "signature": "(adc)"}, {"fullname": "jupyterpidaq.Boards.Simulated.ADCsim_line.Board_ADCsim_line.getsensors", "modulename": "jupyterpidaq.Boards.Simulated.ADCsim_line", "qualname": "Board_ADCsim_line.getsensors", "kind": "function", "doc": "

          Return a list of valid sensor object names for this board.

          \n\n
          Returns
          \n\n
          \n

          list of classnames

          \n
          \n", "signature": "(self):", "funcdef": "def"}, {"fullname": "jupyterpidaq.Boards.Simulated.ADCsim_line.Board_ADCsim_line.V_oversampchan", "modulename": "jupyterpidaq.Boards.Simulated.ADCsim_line", "qualname": "Board_ADCsim_line.V_oversampchan", "kind": "function", "doc": "

          This routine returns the average voltage for the channel\naveraged at (0.0012 + 1/data_rate)^-1 Hz for avg_sec\nnumber of seconds. The 0.0012 is the required loop time\non a RPI 3B+ in python3. The voltage is rounded to the number\nof decimals indicated by the standard deviation. The standard\ndeviation and the estimated deviation of the mean are also\nreturned.\nParameters\n chan the channel number 0, 1, 2, 3\n gain 2/3 (+/-6.144V), 1(+/-4.096V), 2(+/-2.048V), 4(+/-1.024V),\n 8 (+/-0.512V), 16 (+/-0.256V)\n data_rate the ADC sample rate in Hz (8, 16, 32, 64, 128, 250, 475\n or 860 Hz)\n avg_sec seconds to average for, actual averaging interval will be\n as close as possible for an integer number of samples.\nReturns a tuple (V_avg, V_min, V_max, time_stamp)\n V_avg the averaged voltage\n stdev estimated standard deviation of the measurements\n stdev_avg estimated standard deviation of the mean\n time_stamp the time at halfway through the averaging interval in\n seconds since the beginning of the epoch (OS dependent begin\n time).

          \n", "signature": "(self, chan, gain, avg_sec, data_rate=475):", "funcdef": "def"}, {"fullname": "jupyterpidaq.Boards.Simulated.ADCsim_line.Board_ADCsim_line.V_oversampchan_stats", "modulename": "jupyterpidaq.Boards.Simulated.ADCsim_line", "qualname": "Board_ADCsim_line.V_oversampchan_stats", "kind": "function", "doc": "

          This routine returns the average voltage for the channel\naveraged at (0.0012 + 1/data_rate)^-1 Hz for avg_sec\nnumber of seconds. The 0.0012 is the required loop time\non a RPI 3B+ in python3. The voltage is rounded to the number\nof decimals indicated by the standard deviation. The standard\ndeviation and the estimated deviation of the mean are also\nreturned.\nParameters\n chan the channel number 0, 1, 2, 3\n gain 2/3 (+/-6.144V), 1(+/-4.096V), 2(+/-2.048V), 4(+/-1.024V),\n 8 (+/-0.512V), 16 (+/-0.256V)\n data_rate the ADC sample rate in Hz (8, 16, 32, 64, 128, 250, 475\n or 860 Hz)\n avg_sec seconds to average for, actual averaging interval will be\n as close as possible for an integer number of samples.\nReturns a tuple (V_avg, V_min, V_max, time_stamp)\n V_avg the averaged voltage\n stdev estimated standard deviation of the measurements\n stdev_avg estimated standard deviation of the mean\n time_stamp the time at halfway through the averaging interval in\n seconds since the beginning of the epoch (OS dependent begin\n time).

          \n", "signature": "(self, chan, gain, avg_sec, data_rate=475):", "funcdef": "def"}, {"fullname": "jupyterpidaq.Boards.Simulated.ADCsim_line.Board_ADCsim_line.V_sampchan", "modulename": "jupyterpidaq.Boards.Simulated.ADCsim_line", "qualname": "Board_ADCsim_line.V_sampchan", "kind": "function", "doc": "

          This routine returns the average voltage for the channel\naveraged at (0.0012 + 1/data_rate)^-1 Hz for avg_sec\nnumber of seconds. The 0.0012 is the required loop time\non a RPI 3B+ in python3. The voltage is rounded to the number\nof decimals indicated by the standard deviation. The standard\ndeviation and the estimated deviation of the mean are also\nreturned.\nParameters\n chan the channel number 0, 1, 2, 3\n gain 2/3 (+/-6.144V), 1(+/-4.096V), 2(+/-2.048V), 4(+/-1.024V),\n 8 (+/-0.512V), 16 (+/-0.256V)\n data_rate the ADC sample rate in Hz (8, 16, 32, 64, 128, 250, 475\n or 860 Hz) avg_sec seconds to average for, actual averaging\n interval will be as close as possible for an integer number of\n samples.\nReturns a tuple (V_avg, V_min, V_max, time_stamp)\n V_avg the averaged voltage\n stdev estimated standard deviation of the measurements\n stdev_avg estimated standard deviation of the mean\n time_stamp the time at halfway through the averaging interval in\n seconds since the beginning of the epoch (OS dependent begin\n time).

          \n", "signature": "(self, chan, gain, data_rate=475):", "funcdef": "def"}, {"fullname": "jupyterpidaq.Boards.boards", "modulename": "jupyterpidaq.Boards.boards", "kind": "module", "doc": "

          This file handles loading adc board control software and sensor information.\nIt uses the list of known boards. It will skip boards that produce an error\neither because the pypi package is not installed or an error occurs when\ntrying to communicate with the board.

          \n\n

          The ADC simulator will be installed if no boards are available.

          \n"}, {"fullname": "jupyterpidaq.Boards.boards.load_boards", "modulename": "jupyterpidaq.Boards.boards", "qualname": "load_boards", "kind": "function", "doc": "

          Uses the list of known board packages to search for available boards.\nThe file .py should at minimum\nimplement a find_boards(): routine that overrides the function below and\ndefine a class for the particular board that extends theBoard` class\ndefined below.

          \n\n
          Returns
          \n\n
          \n

          list of adc board objects.

          \n
          \n", "signature": "():", "funcdef": "def"}, {"fullname": "jupyterpidaq.Boards.boards.find_boards", "modulename": "jupyterpidaq.Boards.boards", "qualname": "find_boards", "kind": "function", "doc": "

          A function overriding this must be implemented by all board packages.\nSee examples in working packages. This is highly board dependent.

          \n\n
          Returns
          \n\n
          \n

          list of board objects

          \n
          \n", "signature": "():", "funcdef": "def"}, {"fullname": "jupyterpidaq.Boards.boards.Board", "modulename": "jupyterpidaq.Boards.boards", "qualname": "Board", "kind": "class", "doc": "

          Base class for all boards. Each board should be an extension of this class.

          \n"}, {"fullname": "jupyterpidaq.Boards.boards.Board.__init__", "modulename": "jupyterpidaq.Boards.boards", "qualname": "Board.__init__", "kind": "function", "doc": "

          Should be overridden by each board and define at minimum:\nself.name = 'board name/adc name/type' Short an useful to end user\nself.vendor = 'Vendor/Manufacturer name`\nself.channels = tuple of available channel IDs\nself.gains = list of gains\nself.Vdd = voltage provided by board to sensors

          \n", "signature": "()"}, {"fullname": "jupyterpidaq.Boards.boards.Board.getname", "modulename": "jupyterpidaq.Boards.boards", "qualname": "Board.getname", "kind": "function", "doc": "
          Returns
          \n\n
          \n

          string value of the board name, a short label of board type.

          \n
          \n", "signature": "(self):", "funcdef": "def"}, {"fullname": "jupyterpidaq.Boards.boards.Board.getchannels", "modulename": "jupyterpidaq.Boards.boards", "qualname": "Board.getchannels", "kind": "function", "doc": "
          Returns
          \n\n
          \n

          tuple of ids for available channels

          \n
          \n", "signature": "(self):", "funcdef": "def"}, {"fullname": "jupyterpidaq.Boards.boards.Board.getgains", "modulename": "jupyterpidaq.Boards.boards", "qualname": "Board.getgains", "kind": "function", "doc": "

          If not defined for a specific board the gain is fixed at 1.

          \n\n
          Returns
          \n\n
          \n

          tuple of gains availabe for onboard preamp

          \n
          \n", "signature": "(self):", "funcdef": "def"}, {"fullname": "jupyterpidaq.Boards.boards.Board.getvendor", "modulename": "jupyterpidaq.Boards.boards", "qualname": "Board.getvendor", "kind": "function", "doc": "
          Returns
          \n\n
          \n

          string value of the vendor name

          \n
          \n", "signature": "(self):", "funcdef": "def"}, {"fullname": "jupyterpidaq.Boards.boards.Board.getVdd", "modulename": "jupyterpidaq.Boards.boards", "qualname": "Board.getVdd", "kind": "function", "doc": "
          Returns
          \n\n
          \n

          numerical value of the Vdd, voltage provided to sensors

          \n
          \n", "signature": "(self):", "funcdef": "def"}, {"fullname": "jupyterpidaq.Boards.boards.Board.getsensors", "modulename": "jupyterpidaq.Boards.boards", "qualname": "Board.getsensors", "kind": "function", "doc": "

          This returns a list of objects that allow the software to translate\nthe measured voltage into a sensor reading in appropriate units.\nMust be provided by the specific board implementation. See examples\nin working board packages.

          \n\n
          Returns
          \n\n
          \n

          A list of valid sensor objects to use with this board. This\n should be a subset of all the sensors returned by the listSensors\n function in sensors.py.

          \n
          \n", "signature": "(self):", "funcdef": "def"}, {"fullname": "jupyterpidaq.Boards.boards.Board.V_oversampchan", "modulename": "jupyterpidaq.Boards.boards", "qualname": "Board.V_oversampchan", "kind": "function", "doc": "

          This function should return a tuple with average, minimum and maximum\nfor a channel averaged over the period of time avg_sec. How the\naveraging is performed will depend on the board.

          \n\n
          Parameters
          \n\n
            \n
          • chan: id of the channel to be measured
          • \n
          • gain: gain of the channel if adjustable
          • \n
          • avg_sec: float period of time over which to average
          • \n
          \n\n
          Returns
          \n\n
          \n

          a tuple consisting of V_avg, V_min, V_max, time_stamp, avg_Vdd\n The time_stamp is the time the data was collected, usually the\n middle of the averaging period. avg_Vdd should be the measured\n average Vdd taken simultaneously, immediately before,\n or immediately after the voltage being measured. If the board or\n power supply is very stable self.Vdd can be returned instead.

          \n
          \n", "signature": "(self, chan, gain, avg_sec, **kwargs):", "funcdef": "def"}, {"fullname": "jupyterpidaq.Boards.boards.Board.V_oversampchan_stats", "modulename": "jupyterpidaq.Boards.boards", "qualname": "Board.V_oversampchan_stats", "kind": "function", "doc": "

          This function should return a tuple of statistical information for a\nchannel averaged over the period of time avg_sec.

          \n\n
          Parameters
          \n\n
            \n
          • chan: id of the channel to be measured
          • \n
          • gain: gain of the channel if adjustable
          • \n
          • avg_sec: float period of time over which to average
          • \n
          \n\n
          Returns
          \n\n
          \n

          tuple consisting of V_avg, stdev, stdev_avg, time_stamp,\n avg_Vdd where stdev_avg is the estimated standard deviation\n of the average not the standard deviation of the values\n sampled (stdev). avg_Vdd should be the measured\n average Vdd taken simultaneously, immediately before,\n or immediately after the voltage being measured. If the board or\n power supply is very stable self.Vdd can be returned instead.

          \n
          \n", "signature": "(self, chan, gain, avg_sec, **kwargs):", "funcdef": "def"}, {"fullname": "jupyterpidaq.Boards.boards.Board.V_sampchan", "modulename": "jupyterpidaq.Boards.boards", "qualname": "Board.V_sampchan", "kind": "function", "doc": "

          This function returns a single measurement and the time it was\ncollected.

          \n\n
          Parameters
          \n\n
            \n
          • chan: id of the channel to be measured
          • \n
          • gain: gain of the channel if adjustable
          • \n
          \n\n
          Returns
          \n\n
          \n

          a tuple consisting of V, time_stamp, ref_Vdd, where V = the\n single voltage measurement and time_stamp the time it was\n collected. ref_Vdd should be the measured\n Vdd taken simultaneously, immediately before,\n or immediately after the voltage being measured. If the board or\n power supply is very stable self.Vdd can be returned instead.

          \n
          \n", "signature": "(self, chan, gain, **kwargs):", "funcdef": "def"}, {"fullname": "jupyterpidaq.Boards.vernier", "modulename": "jupyterpidaq.Boards.vernier", "kind": "module", "doc": "

          This module contains modules for the Vernier Interfaces.

          \n"}, {"fullname": "jupyterpidaq.Boards.vernier.labquest", "modulename": "jupyterpidaq.Boards.vernier.labquest", "kind": "module", "doc": "

          \n"}, {"fullname": "jupyterpidaq.Boards.vernier.labquest.find_boards", "modulename": "jupyterpidaq.Boards.vernier.labquest", "qualname": "find_boards", "kind": "function", "doc": "

          A rountine like this must be implemented by all board packages.

          \n\n
          Returns
          \n\n
          \n

          list of LabQuests (Types too?)

          \n
          \n", "signature": "():", "funcdef": "def"}, {"fullname": "jupyterpidaq.Boards.vernier.labquest.Board_LQ", "modulename": "jupyterpidaq.Boards.vernier.labquest", "qualname": "Board_LQ", "kind": "class", "doc": "

          Class defining the properties of the analog-to-digital block of the\nLabQuests. Key characteristics:

          \n\n
            \n
          • 3 channels (1, 2, 3) a range of +/- 10 V.
          • \n
          • 12 bit resolution
          • \n
          • Other available but not implemented facilities are Digital I/O.
          • \n
          \n", "bases": "jupyterpidaq.Boards.boards.Board"}, {"fullname": "jupyterpidaq.Boards.vernier.labquest.Board_LQ.__init__", "modulename": "jupyterpidaq.Boards.vernier.labquest", "qualname": "Board_LQ.__init__", "kind": "function", "doc": "

          Should be overridden by each board and define at minimum:\nself.name = 'board name/adc name/type' Short an useful to end user\nself.vendor = 'Vendor/Manufacturer name`\nself.channels = tuple of available channel IDs\nself.gains = list of gains\nself.Vdd = voltage provided by board to sensors

          \n", "signature": "(addr, send, rcv)"}, {"fullname": "jupyterpidaq.Boards.vernier.labquest.Board_LQ.getsensors", "modulename": "jupyterpidaq.Boards.vernier.labquest", "qualname": "Board_LQ.getsensors", "kind": "function", "doc": "

          Return a list of valid sensor object names for this board.

          \n\n
          Returns
          \n\n
          \n

          list of classnames

          \n
          \n", "signature": "(self):", "funcdef": "def"}, {"fullname": "jupyterpidaq.Boards.vernier.labquest.Board_LQ.V_oversampchan", "modulename": "jupyterpidaq.Boards.vernier.labquest", "qualname": "Board_LQ.V_oversampchan", "kind": "function", "doc": "

          This routine returns the average voltage for the channel\naveraged at the default rate for the board and returns an\naverage and observed range.

          \n\n

          Returns a tuple of the following 5 objects:\n V_avg -- float, the averaged voltage

          \n\n
          V_min -- float, the minimum voltage read during\nthe interval\n\nV_max -- float, the maximum voltage read during the\ninterval\n\ntime_stamp -- float, the time at halfway through the averaging\ninterval in seconds since the beginning of the epoch (OS\ndependent begin time)\n\nVdd_avg -- float, the reference voltage (Vdd) collected\nsimultaneously.\n
          \n\n
          Parameters
          \n\n
            \n
          • int chan: the channel number (1, 2, 3)

          • \n
          • gain: ignored by board. Defaults to 1.

          • \n
          • int data_rate: maximum

          • \n
          • float avg_sec: seconds to average for, actual\naveraging interval will be as close as possible for an integer\nnumber of samples

          • \n
          \n\n

          :returns: V_avg, V_min, V_max, time_stamp, Vdd_avg

          \n\n
          Returns
          \n\n
          \n

          description

          \n
          \n\n
          Returns
          \n\n
          \n

          \n
          \n\n
          Returns
          \n\n
          \n

          \n
          \n\n
          Returns
          \n\n
          \n

          \n
          \n\n
          Returns
          \n", "signature": "(self, chan, gain, avg_sec, data_rate=500):", "funcdef": "def"}, {"fullname": "jupyterpidaq.Boards.vernier.labquest.Board_LQ.V_oversampchan_stats", "modulename": "jupyterpidaq.Boards.vernier.labquest", "qualname": "Board_LQ.V_oversampchan_stats", "kind": "function", "doc": "

          This routine returns the average voltage for the channel\naveraged at the maximum rate for the board. The standard\ndeviation and the estimated deviation of the mean are also\nreturned.

          \n\n

          Returns a tuple of the following 5 objects:\n V_avg -- float, the averaged voltage

          \n\n
          stdev -- float, the standard deviation of the measured values\nduring the averaging interval\n\nstdev_avg -- float, the estimated standard deviation of the\nreturned average\n\ntime_stamp -- float, the time at halfway through the averaging\ninterval in seconds since the beginning of the epoch (OS\ndependent begin time)\n\nVdd_avg -- float, returned as None.\n
          \n\n
          Parameters
          \n\n
            \n
          • int chan: the channel number (1, 2, 3)

          • \n
          • gain: ignored by board. Defaults to 1.

          • \n
          • int data_rate:

          • \n
          • float avg_sec: seconds to average for, actual\naveraging interval will be as close as possible for an integer\nnumber of samples

          • \n
          \n\n

          :returns: V_avg, stdev, stdev_avg, time_stamp, Vdd_avg

          \n\n
          Returns
          \n\n
          \n

          \n
          \n\n
          Returns
          \n\n
          \n

          \n
          \n\n
          Returns
          \n\n
          \n

          \n
          \n\n
          Returns
          \n\n
          \n

          \n
          \n\n
          Returns
          \n", "signature": "(self, chan, gain, avg_sec, data_rate=500):", "funcdef": "def"}, {"fullname": "jupyterpidaq.Boards.vernier.labquest.Board_LQ.V_sampchan", "modulename": "jupyterpidaq.Boards.vernier.labquest", "qualname": "Board_LQ.V_sampchan", "kind": "function", "doc": "

          This routine returns a single reading of the voltage for the channel.

          \n\n

          Returns a tuple of the following 5 objects:\n V -- float, the measured voltage

          \n\n
          time_stamp -- float, the time of the measurement in seconds since\nthe beginning of the epoch (OS dependent begin time)\n\nref -- float, the reference voltage (Vdd) collected\nsimultaneously.\n
          \n\n
          Parameters
          \n\n
            \n
          • int chan: the channel number (1, 2, 3).

          • \n
          • gain: ignored by board. Defaults to 1.

          • \n
          • int data_rate:

          • \n
          \n\n

          :returns float value:

          \n\n
          Returns
          \n\n
          \n

          \n
          \n\n
          Returns
          \n", "signature": "(self, chan, gain, data_rate=500):", "funcdef": "def"}, {"fullname": "jupyterpidaq.Boards.vernier.labquest.LQProc", "modulename": "jupyterpidaq.Boards.vernier.labquest", "qualname": "LQProc", "kind": "function", "doc": "

          Process to spawn that continuously collects from the LabQuests(s)

          \n\n

          Parameters

          \n\n

          cmdrcv: Pipe\n Where commands are received.

          \n\n

          datasend: Pipe\n Where data is sent in response to a command.

          \n", "signature": "(cmdrcv, datasend, starttime, samples):", "funcdef": "def"}, {"fullname": "jupyterpidaq.ChannelSettings", "modulename": "jupyterpidaq.ChannelSettings", "kind": "module", "doc": "

          \n"}, {"fullname": "jupyterpidaq.ChannelSettings.ChannelSettings", "modulename": "jupyterpidaq.ChannelSettings", "qualname": "ChannelSettings", "kind": "class", "doc": "

          This class takes care of interacting with the user to get settings for data\ncollection on each channel. Should be initialized with an idno, so that\nit knows what number has been assigned to it.

          \n"}, {"fullname": "jupyterpidaq.ChannelSettings.ChannelSettings.__init__", "modulename": "jupyterpidaq.ChannelSettings", "qualname": "ChannelSettings.__init__", "kind": "function", "doc": "
          Parameters
          \n\n
            \n
          • idno: int number iding this instance of ChannelSettings
          • \n
          \n", "signature": "(idno, availboards)"}, {"fullname": "jupyterpidaq.ChannelSettings.ChannelSettings.activate", "modulename": "jupyterpidaq.ChannelSettings", "qualname": "ChannelSettings.activate", "kind": "function", "doc": "

          This function makes this channel active. No return value unless an e\nrror is thrown by something called by this function.

          \n\n
          Returns
          \n\n
          \n

          None

          \n
          \n", "signature": "(self):", "funcdef": "def"}, {"fullname": "jupyterpidaq.ChannelSettings.ChannelSettings.deactivate", "modulename": "jupyterpidaq.ChannelSettings", "qualname": "ChannelSettings.deactivate", "kind": "function", "doc": "

          This function makes the channel inactive. No return value unless an\nerror is thrown by something called by this function.

          \n\n
          Returns
          \n\n
          \n

          None

          \n
          \n", "signature": "(self):", "funcdef": "def"}, {"fullname": "jupyterpidaq.ChannelSettings.ChannelSettings.checkchanged", "modulename": "jupyterpidaq.ChannelSettings", "qualname": "ChannelSettings.checkchanged", "kind": "function", "doc": "

          This function is called when the checkbox changes.

          \n\n
          Parameters
          \n\n
            \n
          • self:
          • \n
          • change: change object passed by the observe tool
          • \n
          \n\n
          Returns
          \n\n
          \n

          None

          \n
          \n", "signature": "(self, change):", "funcdef": "def"}, {"fullname": "jupyterpidaq.ChannelSettings.ChannelSettings.boardchanged", "modulename": "jupyterpidaq.ChannelSettings", "qualname": "ChannelSettings.boardchanged", "kind": "function", "doc": "

          This function responds to a change in board choice.

          \n\n
          Parameters
          \n\n
            \n
          • change: change object passed by the observe tool
          • \n
          \n\n
          Returns
          \n", "signature": "(self, change):", "funcdef": "def"}, {"fullname": "jupyterpidaq.ChannelSettings.ChannelSettings.channelchanged", "modulename": "jupyterpidaq.ChannelSettings", "qualname": "ChannelSettings.channelchanged", "kind": "function", "doc": "

          This function responds to a change in board choice.

          \n\n
          Parameters
          \n\n
            \n
          • change: change object passed by the observe tool
          • \n
          \n\n
          Returns
          \n", "signature": "(self, change):", "funcdef": "def"}, {"fullname": "jupyterpidaq.ChannelSettings.ChannelSettings.sensorchanged", "modulename": "jupyterpidaq.ChannelSettings", "qualname": "ChannelSettings.sensorchanged", "kind": "function", "doc": "

          Called by the observe function of sensorchoice when the user changes\nthe sensor choice.

          \n\n
          Parameters
          \n\n
            \n
          • self:
          • \n
          • change: change object passed by the observe tool
          • \n
          \n\n
          Returns
          \n\n
          \n

          None

          \n
          \n", "signature": "(self, change):", "funcdef": "def"}, {"fullname": "jupyterpidaq.ChannelSettings.ChannelSettings.unitschanged", "modulename": "jupyterpidaq.ChannelSettings", "qualname": "ChannelSettings.unitschanged", "kind": "function", "doc": "

          Called by the observe function for the units selector when units are\nchanged

          \n\n
          Parameters
          \n\n
            \n
          • self:
          • \n
          • change: change object passed by the observe tool
          • \n
          \n\n
          Returns
          \n", "signature": "(self, change):", "funcdef": "def"}, {"fullname": "jupyterpidaq.ChannelSettings.ChannelSettings.gainschanged", "modulename": "jupyterpidaq.ChannelSettings", "qualname": "ChannelSettings.gainschanged", "kind": "function", "doc": "

          Called by the observe function for the gains selector when the gain is\nchanged.

          \n\n
          Parameters
          \n\n
            \n
          • self:
          • \n
          • change: change object passed by the observe tool
          • \n
          \n\n
          Returns
          \n", "signature": "(self, change):", "funcdef": "def"}, {"fullname": "jupyterpidaq.ChannelSettings.ChannelSettings.setup", "modulename": "jupyterpidaq.ChannelSettings", "qualname": "ChannelSettings.setup", "kind": "function", "doc": "

          Sets up the GUI and the necessary monitoring.

          \n\n
          Returns
          \n\n
          \n

          None

          \n
          \n", "signature": "(self):", "funcdef": "def"}, {"fullname": "jupyterpidaq.ChannelSettings.ChannelSettings.hideGUI", "modulename": "jupyterpidaq.ChannelSettings", "qualname": "ChannelSettings.hideGUI", "kind": "function", "doc": "

          \n", "signature": "(self):", "funcdef": "def"}, {"fullname": "jupyterpidaq.DAQProc", "modulename": "jupyterpidaq.DAQProc", "kind": "module", "doc": "

          \n"}, {"fullname": "jupyterpidaq.DAQProc.DAQProc", "modulename": "jupyterpidaq.DAQProc", "qualname": "DAQProc", "kind": "function", "doc": "

          This function is to be run in a separate thread to asynchronously\ncommunicate with the ADC board.

          \n\n
          Parameters
          \n\n
            \n
          • list whichchn: a list of dictionaries. Each dictionary is of the\nform:{'board': board_object, 'chnl': chnlID}.

          • \n
          • list gains: a list of the numerical gain for each channel.

          • \n
          • float avgtime: the averaging time in seconds for a data point.

          • \n
          • float timedelta: the target time between data points.

          • \n
          • pipe DAQconn: the connection pipe

          • \n
          • pipe DAQCTL: the control pipe

          • \n
          \n\n
          Returns
          \n\n
          \n

          Data is returned via the pipes.\n On the DAQCTL pipe this only returns 'done'\n On the DAQconn pipe a list of lists with data is returned.

          \n
          \n", "signature": "(whichchn, gains, avgtime, timedelta, DAQconn, DAQCTL):", "funcdef": "def"}, {"fullname": "jupyterpidaq.DAQinstance", "modulename": "jupyterpidaq.DAQinstance", "kind": "module", "doc": "

          \n"}, {"fullname": "jupyterpidaq.DAQinstance.DAQinstance", "modulename": "jupyterpidaq.DAQinstance", "qualname": "DAQinstance", "kind": "class", "doc": "

          \n"}, {"fullname": "jupyterpidaq.DAQinstance.DAQinstance.__init__", "modulename": "jupyterpidaq.DAQinstance", "qualname": "DAQinstance.__init__", "kind": "function", "doc": "

          Data Aquistion Instance (a run).

          \n\n
          Parameters
          \n\n
            \n
          • idno: id number you wish to use to keep track
          • \n
          • title: optional name
          • \n
          • ntraces: number of traces (default = 4) more than 4 easily\noverwhelms a pi4.
          • \n
          • kwargs: \n:ignore_skew: bool (default: True) if True only a single average\ncollection time will be recorded for each time in a multichannel\ndata collection. If False a separate set of time will be\nrecorded for each channel.
          • \n
          \n", "signature": "(idno, title='None', ntraces=4, **kwargs)"}, {"fullname": "jupyterpidaq.DAQinstance.DAQinstance.setupclick", "modulename": "jupyterpidaq.DAQinstance", "qualname": "DAQinstance.setupclick", "kind": "function", "doc": "

          \n", "signature": "(self, btn):", "funcdef": "def"}, {"fullname": "jupyterpidaq.DAQinstance.DAQinstance.setup", "modulename": "jupyterpidaq.DAQinstance", "qualname": "DAQinstance.setup", "kind": "function", "doc": "

          \n", "signature": "(self):", "funcdef": "def"}, {"fullname": "jupyterpidaq.DAQinstance.DAQinstance.collectclick", "modulename": "jupyterpidaq.DAQinstance", "qualname": "DAQinstance.collectclick", "kind": "function", "doc": "

          \n", "signature": "(self, btn):", "funcdef": "def"}, {"fullname": "jupyterpidaq.DAQinstance.DAQinstance.fillpandadf", "modulename": "jupyterpidaq.DAQinstance", "qualname": "DAQinstance.fillpandadf", "kind": "function", "doc": "

          \n", "signature": "(self):", "funcdef": "def"}, {"fullname": "jupyterpidaq.DAQinstance.DAQinstance.updatingplot", "modulename": "jupyterpidaq.DAQinstance", "qualname": "DAQinstance.updatingplot", "kind": "function", "doc": "

          Runs until a check of self.collectbtn.description does not return\n'Stop Collecting'. This would probably be more efficient if set a\nboolean.

          \n\n

          Parameters

          \n\n

          PLTconn: Pipe\n connection plotter end\nDAQconn: Pipe\n connection DAQ end\nPLTCTL: Pipe\n control pipe plotter end\nDAQCTL: Pipe\n control pipe DAQ end

          \n", "signature": "(self, PLTconn, PLTCTL):", "funcdef": "def"}, {"fullname": "jupyterpidaq.DAQinstance.Run", "modulename": "jupyterpidaq.DAQinstance", "qualname": "Run", "kind": "function", "doc": "

          Load a run from stored data or start a new run if the local file for\nthe run does not exist.

          \n\n

          Parameters

          \n\n

          name: str\n String name for the run. The data will be stored in a file of this\n name with the extension of .jpidaq.html.

          \n", "signature": "(name):", "funcdef": "def"}, {"fullname": "jupyterpidaq.DAQinstance.doRun", "modulename": "jupyterpidaq.DAQinstance", "qualname": "doRun", "kind": "function", "doc": "

          \n", "signature": "(whichrun):", "funcdef": "def"}, {"fullname": "jupyterpidaq.DAQinstance.update_runsdrp", "modulename": "jupyterpidaq.DAQinstance", "qualname": "update_runsdrp", "kind": "function", "doc": "

          \n", "signature": "():", "funcdef": "def"}, {"fullname": "jupyterpidaq.DAQinstance.showSelectedRunTable", "modulename": "jupyterpidaq.DAQinstance", "qualname": "showSelectedRunTable", "kind": "function", "doc": "

          \n", "signature": "(change):", "funcdef": "def"}, {"fullname": "jupyterpidaq.DAQinstance.showDataTable", "modulename": "jupyterpidaq.DAQinstance", "qualname": "showDataTable", "kind": "function", "doc": "

          Provides a menu to select which run. Then displays the run in a\n10 em high scrolling table. Selection menu is removed after choice\nis made.

          \n", "signature": "():", "funcdef": "def"}, {"fullname": "jupyterpidaq.DAQinstance.newCalculatedColumn", "modulename": "jupyterpidaq.DAQinstance", "qualname": "newCalculatedColumn", "kind": "function", "doc": "

          Uses jupyter-pandas-GUI.new_pandas_column_GUI to provide a GUI expression\ncomposer. This method finds the datasets and launches the GUI.

          \n", "signature": "():", "funcdef": "def"}, {"fullname": "jupyterpidaq.DAQinstance.newPlot", "modulename": "jupyterpidaq.DAQinstance", "qualname": "newPlot", "kind": "function", "doc": "

          Uses jupyter-pandas-GUI.plot_pandas_GUI to provide a GUI expression\ncomposer. This method finds the datasets and launches the GUI.

          \n", "signature": "():", "funcdef": "def"}, {"fullname": "jupyterpidaq.DAQinstance.newFit", "modulename": "jupyterpidaq.DAQinstance", "qualname": "newFit", "kind": "function", "doc": "

          Uses jupyter-pandas-GUI.fit_pandas_GUI to provide a GUI expression\ncomposer. This method finds the datasets and launches the GUI.

          \n", "signature": "():", "funcdef": "def"}, {"fullname": "jupyterpidaq.Sensors", "modulename": "jupyterpidaq.Sensors", "kind": "module", "doc": "

          This module wraps all the modules related to interpretting data from known\nsensors.

          \n"}, {"fullname": "jupyterpidaq.Sensors.sensors", "modulename": "jupyterpidaq.Sensors.sensors", "kind": "module", "doc": "

          \n"}, {"fullname": "jupyterpidaq.Sensors.sensors.to_reasonable_significant_figures", "modulename": "jupyterpidaq.Sensors.sensors", "qualname": "to_reasonable_significant_figures", "kind": "function", "doc": "

          This function will return value rounded to a reasonable number of\nsignificant figures based on the uncertainty. If you are doing this\nbased on the standard return from the raw voltage or the sensor\ndefinitions in this file it is recommend that this be the standard\ndeviation of the average, which will often provide about one more digit\nthan the standard deviation. This will provide a guard digit for further\ncomputations.

          \n\n
          Parameters
          \n\n
            \n
          • float value: the value to be rounded
          • \n
          • float uncertainty: the uncertainty.
          • \n
          \n\n

          :returns float:

          \n\n

          Returns: rounded_value a floating point number.

          \n", "signature": "(value, uncertainty):", "funcdef": "def"}, {"fullname": "jupyterpidaq.Sensors.sensors.to_reasonable_significant_figures_fast", "modulename": "jupyterpidaq.Sensors.sensors", "qualname": "to_reasonable_significant_figures_fast", "kind": "function", "doc": "

          This function will return values rounded to a reasonable number of\nsignificant figures based on the avg_std. This function requires fewer\ncompares so is a little more efficient than calling\nto_reasonable_significant_figures(value, uncertainty) for avg, std,\navg_std separately.

          \n\n
          Parameters
          \n\n
            \n
          • float avg: the average value
          • \n
          • float std: the standard deviation
          • \n
          • float avg_std: the estimated standard deviation in avg\n:returns list:
          • \n
          \n\n

          Returns: list of rounded values for each [avg, std, avg_std]

          \n", "signature": "(avg, std, avg_std):", "funcdef": "def"}, {"fullname": "jupyterpidaq.Sensors.sensors.listSensors", "modulename": "jupyterpidaq.Sensors.sensors", "qualname": "listSensors", "kind": "function", "doc": "

          Provides a list of the sensor classes provided by this file. The list must\nbe manually updated with each new class.

          \n\n
          Returns
          \n\n
          \n

          list of classnames

          \n
          \n", "signature": "():", "funcdef": "def"}, {"fullname": "jupyterpidaq.Sensors.sensors.RawAtoD", "modulename": "jupyterpidaq.Sensors.sensors", "qualname": "RawAtoD", "kind": "class", "doc": "

          This is the base sensor class which all sensors should extend. See how to\ndo this properly using one of the examples below.\nThis class contains definitions for the raw AtoD return in volts. The\ndigital values are not used as the AtoD may have a builtin pre-amp,\nso a given digital value has different meanings depending upon the pre-amp\nsetting.

          \n"}, {"fullname": "jupyterpidaq.Sensors.sensors.RawAtoD.__init__", "modulename": "jupyterpidaq.Sensors.sensors", "qualname": "RawAtoD.__init__", "kind": "function", "doc": "

          This init should be called first in the init section of any class\nextending this class (e.g. super().__init__(Vdd)). Then set\nself.name and self.vendor to the proper values. Append units\nspecific to the sensor to self.units. The parameter Vdd must be\nsupplied upon initialization because the output voltage of some\nsensors depends on Vdd.

          \n\n
          Parameters
          \n\n
            \n
          • float Vdd: the voltage supplied to the sensor by the A-to-D\nboard in case the sensor output depends on this.
          • \n
          \n", "signature": "(Vdd)"}, {"fullname": "jupyterpidaq.Sensors.sensors.RawAtoD.getname", "modulename": "jupyterpidaq.Sensors.sensors", "qualname": "RawAtoD.getname", "kind": "function", "doc": "

          Provides a string name for the sensor

          \n\n
          Returns
          \n\n
          \n

          string containing the sensor name

          \n
          \n", "signature": "(self):", "funcdef": "def"}, {"fullname": "jupyterpidaq.Sensors.sensors.RawAtoD.getvendor", "modulename": "jupyterpidaq.Sensors.sensors", "qualname": "RawAtoD.getvendor", "kind": "function", "doc": "

          Provides a string name for the sensor vendor/manufacturer

          \n\n
          Returns
          \n\n
          \n

          string containing the vendor/manufacturer name

          \n
          \n", "signature": "(self):", "funcdef": "def"}, {"fullname": "jupyterpidaq.Sensors.sensors.RawAtoD.getunits", "modulename": "jupyterpidaq.Sensors.sensors", "qualname": "RawAtoD.getunits", "kind": "function", "doc": "

          Provides the string names for the available units for this sensor.\nThese string names are also the functions within this class that\nreturn the measurement in those units.

          \n\n
          Returns
          \n\n
          \n

          units a list of strings.

          \n
          \n", "signature": "(self):", "funcdef": "def"}, {"fullname": "jupyterpidaq.Sensors.sensors.RawAtoD.V", "modulename": "jupyterpidaq.Sensors.sensors", "qualname": "RawAtoD.V", "kind": "function", "doc": "

          It is not really necessary to call this function because it just\nreturns the same values that are passed to it.\nIt is provided for consistency with the way sensors units are defined.

          \n\n
          Parameters
          \n\n
            \n
          • v_avg: v_avg: average voltage from A-to-D
          • \n
          • v_std: standard deviation of the A-to-D measurements
          • \n
          • avg_std: estimate of the standard deviation of v_avg
          • \n
          • float avg_vdd: simultaneously measured average Vdd.
          • \n
          \n\n
          Returns
          \n\n
          \n

          [v_avg, v_std, avg_std]

          \n
          \n", "signature": "(self, v_avg, v_std, avg_std, avg_vdd):", "funcdef": "def"}, {"fullname": "jupyterpidaq.Sensors.sensors.RawAtoD.mV", "modulename": "jupyterpidaq.Sensors.sensors", "qualname": "RawAtoD.mV", "kind": "function", "doc": "

          Convert the raw AtoD voltage to mV.

          \n\n
          Parameters
          \n\n
            \n
          • v_avg: v_avg: average voltage from A-to-D
          • \n
          • v_std: standard deviation of the A-to-D measurements
          • \n
          • avg_std: estimate of the standard deviation of v_avg
          • \n
          • float avg_vdd: simultaneously measured average Vdd.
          • \n
          \n\n
          Returns
          \n\n
          \n

          [v_avg, v_std, avg_std] converted to mV

          \n
          \n", "signature": "(self, v_avg, v_std, avg_std, avg_vdd):", "funcdef": "def"}, {"fullname": "jupyterpidaq.Sensors.sensors.BuiltInThermistor", "modulename": "jupyterpidaq.Sensors.sensors", "qualname": "BuiltInThermistor", "kind": "class", "doc": "

          This class contains the definitions for builtin thermistor.

          \n", "bases": "RawAtoD"}, {"fullname": "jupyterpidaq.Sensors.sensors.BuiltInThermistor.__init__", "modulename": "jupyterpidaq.Sensors.sensors", "qualname": "BuiltInThermistor.__init__", "kind": "function", "doc": "

          This init should be called first in the init section of any class\nextending this class (e.g. super().__init__(Vdd)). Then set\nself.name and self.vendor to the proper values. Append units\nspecific to the sensor to self.units. The parameter Vdd must be\nsupplied upon initialization because the output voltage of some\nsensors depends on Vdd.

          \n\n
          Parameters
          \n\n
            \n
          • float Vdd: the voltage supplied to the sensor by the A-to-D\nboard in case the sensor output depends on this.
          • \n
          \n", "signature": "(Vdd)"}, {"fullname": "jupyterpidaq.Sensors.sensors.BuiltInThermistor.K", "modulename": "jupyterpidaq.Sensors.sensors", "qualname": "BuiltInThermistor.K", "kind": "function", "doc": "

          The returned values are in K. It is assumed that the distribution is\nsymmetric guassian even in K. This may not be true, but still gives\na reasonable estimate of the standard deviation.

          \n\n
          Parameters
          \n\n
            \n
          • v_avg: average voltage from sensor.
          • \n
          • v_std: standard deviation of voltage from sensor.
          • \n
          • avg_std: estimated standard deviation of the avg.
          • \n
          • float avg_vdd: simultaneously measured average Vdd.
          • \n
          \n\n
          Returns
          \n\n
          \n

          \n
          \n\n

          Returns: list [K_avg, K_std, K_avg_std]\n [average temperature in K,\n standard deviation of temperature in K,\n estimated standard deviation of the average temperature].

          \n", "signature": "(self, v_avg, v_std, avg_std, avg_vdd):", "funcdef": "def"}, {"fullname": "jupyterpidaq.Sensors.sensors.BuiltInThermistor.C", "modulename": "jupyterpidaq.Sensors.sensors", "qualname": "BuiltInThermistor.C", "kind": "function", "doc": "

          The returned values are in deg C. It is assumed that the distribution\nis symmetric guassian even in deg C. This may not be true, but still\ngives a reasonable estimate of the standard deviation.

          \n\n
          Parameters
          \n\n
            \n
          • v_avg: average voltage from sensor.
          • \n
          • v_std: standard deviation of voltage from sensor.
          • \n
          • avg_std: estimated standard deviation of the avg.
          • \n
          • float avg_vdd: simultaneously measured average Vdd.
          • \n
          \n\n
          Returns
          \n\n
          \n

          \n
          \n\n

          Returns: list [C_avg, C_std, C_avg_std]\n [average temperature in C, standard deviation of temperature in C,\n estimated standard deviation of the average temperature].

          \n", "signature": "(self, v_avg, v_std, avg_std, avg_vdd):", "funcdef": "def"}, {"fullname": "jupyterpidaq.Sensors.sensors.BuiltInThermistor.F", "modulename": "jupyterpidaq.Sensors.sensors", "qualname": "BuiltInThermistor.F", "kind": "function", "doc": "

          The returned values are in deg F. It is assumed that the distribution\nis symmetric guassian even in deg F. This may not be true, but still\ngives a reasonable estimate of the standard deviation.

          \n\n
          Parameters
          \n\n
            \n
          • float v_avg: average voltage from sensor.
          • \n
          • float v_std: standard deviation of voltage from sensor.
          • \n
          • float avg_std: estimated standard deviation of the avg.
          • \n
          • float avg_vdd: simultaneously measured average Vdd.\n:returns list:
          • \n
          \n\n

          Returns: list [F_avg, F_std, F_avg_std]\n [average temperature in F, standard deviation of temperature in F,\n estimated standard deviation of the average temperature].

          \n", "signature": "(self, v_avg, v_std, avg_std, avg_vdd):", "funcdef": "def"}, {"fullname": "jupyterpidaq.Sensors.sensors.VernierSSTemp", "modulename": "jupyterpidaq.Sensors.sensors", "qualname": "VernierSSTemp", "kind": "class", "doc": "

          This class contains the definitions for Vernier Stainless Steel Temperature\nProbe. A 20K thermistor.

          \n", "bases": "RawAtoD"}, {"fullname": "jupyterpidaq.Sensors.sensors.VernierSSTemp.__init__", "modulename": "jupyterpidaq.Sensors.sensors", "qualname": "VernierSSTemp.__init__", "kind": "function", "doc": "

          This init should be called first in the init section of any class\nextending this class (e.g. super().__init__(Vdd)). Then set\nself.name and self.vendor to the proper values. Append units\nspecific to the sensor to self.units. The parameter Vdd must be\nsupplied upon initialization because the output voltage of some\nsensors depends on Vdd.

          \n\n
          Parameters
          \n\n
            \n
          • float Vdd: the voltage supplied to the sensor by the A-to-D\nboard in case the sensor output depends on this.
          • \n
          \n", "signature": "(Vdd)"}, {"fullname": "jupyterpidaq.Sensors.sensors.VernierSSTemp.K", "modulename": "jupyterpidaq.Sensors.sensors", "qualname": "VernierSSTemp.K", "kind": "function", "doc": "

          The returned values are in K. It is assumed that the distribution is\nsymmetric guassian even in K. This may not be true, but still gives\na reasonable estimate of the standard deviation.

          \n\n
          Parameters
          \n\n
            \n
          • float v_avg: average voltage from sensor.
          • \n
          • float v_std: standard deviation of voltage from sensor.
          • \n
          • float avg_std: estimated standard deviation of the avg.
          • \n
          • float avg_vdd: simultaneously measured average Vdd.\n:returns list:
          • \n
          \n\n

          Returns list [K_avg, K_std, K_avg_std]\n [average temperature in K, standard deviation of temperature in K,\n estimated standard deviation of the average temperature].

          \n", "signature": "(self, v_avg, v_std, avg_std, avg_vdd):", "funcdef": "def"}, {"fullname": "jupyterpidaq.Sensors.sensors.VernierSSTemp.C", "modulename": "jupyterpidaq.Sensors.sensors", "qualname": "VernierSSTemp.C", "kind": "function", "doc": "

          The returned values are in deg C. It is assumed that the distribution\nis symmetric guassian even in deg C. This may not be true, but still\ngives a reasonable estimate of the standard deviation.

          \n\n
          Parameters
          \n\n
            \n
          • float v_avg: average voltage from sensor.
          • \n
          • float v_std: standard deviation of voltage from sensor.
          • \n
          • float avg_std: estimated standard deviation of the avg.
          • \n
          • float avg_vdd: simultaneously measured average Vdd.
          • \n
          \n\n
          Returns
          \n\n
          \n

          \n
          \n\n

          Returns: C_avg, C_std, C_avg_std\n average temperature in C, standard deviation of temperature in C,\n estimated standard deviation of the average temperature.

          \n", "signature": "(self, v_avg, v_std, avg_std, avg_vdd):", "funcdef": "def"}, {"fullname": "jupyterpidaq.Sensors.sensors.VernierSSTemp.F", "modulename": "jupyterpidaq.Sensors.sensors", "qualname": "VernierSSTemp.F", "kind": "function", "doc": "

          The returned values are in deg F. It is assumed that the\ndistribution is symmetric guassian even in deg F. This may not be\ntrue, but still gives a reasonable estimate of the standard\ndeviation.

          \n\n
          Parameters
          \n\n
            \n
          • float v_avg: average voltage from sensor.
          • \n
          • float v_std: standard deviation of voltage from sensor.
          • \n
          • float avg_std: estimated standard deviation of the avg.
          • \n
          • float avg_vdd: simultaneously measured average Vdd.
          • \n
          \n\n
          Returns
          \n\n
          \n

          \n
          \n\n

          Returns: F_avg, F_std, F_avg_std\n average temperature in F, standard deviation of\n temperature in F, estimated standard deviation of the average\n temperature.

          \n", "signature": "(self, v_avg, v_std, avg_std, avg_vdd):", "funcdef": "def"}, {"fullname": "jupyterpidaq.Sensors.sensors.VernierGasP", "modulename": "jupyterpidaq.Sensors.sensors", "qualname": "VernierGasP", "kind": "class", "doc": "

          This class contains the definitions for Vernier absolute gas pressure\nsensor, GPS-BTA (post 2011 manufacture).

          \n", "bases": "RawAtoD"}, {"fullname": "jupyterpidaq.Sensors.sensors.VernierGasP.__init__", "modulename": "jupyterpidaq.Sensors.sensors", "qualname": "VernierGasP.__init__", "kind": "function", "doc": "

          This init should be called first in the init section of any class\nextending this class (e.g. super().__init__(Vdd)). Then set\nself.name and self.vendor to the proper values. Append units\nspecific to the sensor to self.units. The parameter Vdd must be\nsupplied upon initialization because the output voltage of some\nsensors depends on Vdd.

          \n\n
          Parameters
          \n\n
            \n
          • float Vdd: the voltage supplied to the sensor by the A-to-D\nboard in case the sensor output depends on this.
          • \n
          \n", "signature": "(Vdd)"}, {"fullname": "jupyterpidaq.Sensors.sensors.VernierGasP.Pa", "modulename": "jupyterpidaq.Sensors.sensors", "qualname": "VernierGasP.Pa", "kind": "function", "doc": "
          Parameters
          \n\n
            \n
          • float v_avg: average raw voltage
          • \n
          • float v_std: standard deviation of the raw voltage
          • \n
          • float avg_std: estimated standard deviation of v_avg
          • \n
          • float avg_vdd: the Vdd measured simultaneously with v_avg (not\nused)
          • \n
          \n\n
          Returns
          \n\n
          \n

          \n
          \n\n

          Returns: P_avg, P_std, P_avg_std all in Pascals

          \n", "signature": "(self, v_avg, v_std, avg_std, avg_vdd):", "funcdef": "def"}, {"fullname": "jupyterpidaq.Sensors.sensors.VernierGasP.kPa", "modulename": "jupyterpidaq.Sensors.sensors", "qualname": "VernierGasP.kPa", "kind": "function", "doc": "
          Parameters
          \n\n
            \n
          • float v_avg: average raw voltage
          • \n
          • float v_std: standard deviation of the raw voltage
          • \n
          • float avg_std: estimated standard deviation of v_avg
          • \n
          • float avg_vdd: the Vdd measured simultaneously with v_avg (not\nused)
          • \n
          \n\n
          Returns
          \n\n
          \n

          \n
          \n\n

          Returns: P_avg, P_std, P_avg_std all in kiloPascals

          \n", "signature": "(self, v_avg, v_std, avg_std, avg_vdd):", "funcdef": "def"}, {"fullname": "jupyterpidaq.Sensors.sensors.VernierGasP.Bar", "modulename": "jupyterpidaq.Sensors.sensors", "qualname": "VernierGasP.Bar", "kind": "function", "doc": "
          Parameters
          \n\n
            \n
          • float v_avg: average raw voltage
          • \n
          • float v_std: standard deviation of the raw voltage
          • \n
          • float avg_std: estimated standard deviation of v_avg
          • \n
          • float avg_vdd: the Vdd measured simultaneously with v_avg (not\nused)
          • \n
          \n\n
          Returns
          \n\n
          \n

          \n
          \n\n

          Returns: P_avg, P_std, P_avg_std all in Bars

          \n", "signature": "(self, v_avg, v_std, avg_std, avg_vdd):", "funcdef": "def"}, {"fullname": "jupyterpidaq.Sensors.sensors.VernierGasP.Torr", "modulename": "jupyterpidaq.Sensors.sensors", "qualname": "VernierGasP.Torr", "kind": "function", "doc": "
          Parameters
          \n\n
            \n
          • float v_avg: average raw voltage
          • \n
          • float v_std: standard deviation of the voltage measurements
          • \n
          • float avg_std: estimate of the standard deviation of the average
          • \n
          • float avg_vdd: the Vdd measured simultaneously with v_avg (\nnot used)
          • \n
          \n\n
          Returns
          \n\n
          \n

          \n
          \n\n

          Returns: P_avg, P_std, P_avg_std all in Torr

          \n", "signature": "(self, v_avg, v_std, avg_std, avg_vdd):", "funcdef": "def"}, {"fullname": "jupyterpidaq.Sensors.sensors.VernierGasP.mmHg", "modulename": "jupyterpidaq.Sensors.sensors", "qualname": "VernierGasP.mmHg", "kind": "function", "doc": "
          Parameters
          \n\n
            \n
          • float v_avg: average raw voltage
          • \n
          • float v_std: standard deviation of the voltage measurements
          • \n
          • float avg_std: estimate of the standard deviation of the average
          • \n
          • float avg_vdd: the Vdd measured simultaneously with v_avg (\nnot used)
          • \n
          \n\n
          Returns
          \n\n
          \n

          \n
          \n\n

          Returns: P_avg, P_std, P_avg_std all in mmHg

          \n", "signature": "(self, v_avg, v_std, avg_std, avg_vdd):", "funcdef": "def"}, {"fullname": "jupyterpidaq.Sensors.sensors.VernierGasP.atm", "modulename": "jupyterpidaq.Sensors.sensors", "qualname": "VernierGasP.atm", "kind": "function", "doc": "
          Parameters
          \n\n
            \n
          • float v_avg: average raw voltage
          • \n
          • float v_std: standard deviation of the voltage measurements
          • \n
          • float avg_std: estimate of the standard deviation of the average
          • \n
          • float avg_vdd: the Vdd measured simultaneously with v_avg (\nnot used)
          • \n
          \n\n
          Returns
          \n\n
          \n

          \n
          \n\n

          Returns: P_avg, P_std, P_avg_std all in atm

          \n", "signature": "(self, v_avg, v_std, avg_std, avg_vdd):", "funcdef": "def"}, {"fullname": "jupyterpidaq.Sensors.sensors.VernierGasP_OLD", "modulename": "jupyterpidaq.Sensors.sensors", "qualname": "VernierGasP_OLD", "kind": "class", "doc": "

          This class contains the definitions for Vernier absolute gas pressure\nsensor, GPS-BTA (pre 2011 manufacture. Label does not depict a caliper\nwith the registered trademark symbol).

          \n", "bases": "RawAtoD"}, {"fullname": "jupyterpidaq.Sensors.sensors.VernierGasP_OLD.__init__", "modulename": "jupyterpidaq.Sensors.sensors", "qualname": "VernierGasP_OLD.__init__", "kind": "function", "doc": "

          This init should be called first in the init section of any class\nextending this class (e.g. super().__init__(Vdd)). Then set\nself.name and self.vendor to the proper values. Append units\nspecific to the sensor to self.units. The parameter Vdd must be\nsupplied upon initialization because the output voltage of some\nsensors depends on Vdd.

          \n\n
          Parameters
          \n\n
            \n
          • float Vdd: the voltage supplied to the sensor by the A-to-D\nboard in case the sensor output depends on this.
          • \n
          \n", "signature": "(Vdd)"}, {"fullname": "jupyterpidaq.Sensors.sensors.VernierGasP_OLD.Pa", "modulename": "jupyterpidaq.Sensors.sensors", "qualname": "VernierGasP_OLD.Pa", "kind": "function", "doc": "
          Parameters
          \n\n
            \n
          • float v_avg: average raw voltage
          • \n
          • float v_std: standard deviation of the raw voltage
          • \n
          • float avg_std: estimated standard deviation of v_avg
          • \n
          • float avg_vdd: the Vdd measured simultaneously with v_avg (not\nused)
          • \n
          \n\n
          Returns
          \n\n
          \n

          \n
          \n\n

          Returns: P_avg, P_std, P_avg_std all in Pascals

          \n", "signature": "(self, v_avg, v_std, avg_std, avg_vdd):", "funcdef": "def"}, {"fullname": "jupyterpidaq.Sensors.sensors.VernierGasP_OLD.kPa", "modulename": "jupyterpidaq.Sensors.sensors", "qualname": "VernierGasP_OLD.kPa", "kind": "function", "doc": "
          Parameters
          \n\n
            \n
          • float v_avg: average raw voltage
          • \n
          • float v_std: standard deviation of the raw voltage
          • \n
          • float avg_std: estimated standard deviation of v_avg
          • \n
          • float avg_vdd: the Vdd measured simultaneously with v_avg (not\nused)
          • \n
          \n\n
          Returns
          \n\n
          \n

          \n
          \n\n

          Returns: P_avg, P_std, P_avg_std all in kiloPascals

          \n", "signature": "(self, v_avg, v_std, avg_std, avg_vdd):", "funcdef": "def"}, {"fullname": "jupyterpidaq.Sensors.sensors.VernierGasP_OLD.Bar", "modulename": "jupyterpidaq.Sensors.sensors", "qualname": "VernierGasP_OLD.Bar", "kind": "function", "doc": "
          Parameters
          \n\n
            \n
          • float v_avg: average raw voltage
          • \n
          • float v_std: standard deviation of the raw voltage
          • \n
          • float avg_std: estimated standard deviation of v_avg
          • \n
          • float avg_vdd: the Vdd measured simultaneously with v_avg (not\nused)
          • \n
          \n\n
          Returns
          \n\n
          \n

          \n
          \n\n

          Returns: P_avg, P_std, P_avg_std all in Bars

          \n", "signature": "(self, v_avg, v_std, avg_std, avg_vdd):", "funcdef": "def"}, {"fullname": "jupyterpidaq.Sensors.sensors.VernierGasP_OLD.Torr", "modulename": "jupyterpidaq.Sensors.sensors", "qualname": "VernierGasP_OLD.Torr", "kind": "function", "doc": "
          Parameters
          \n\n
            \n
          • float v_avg: average raw voltage
          • \n
          • float v_std: standard deviation of the voltage measurements
          • \n
          • float avg_std: estimate of the standard deviation of the average
          • \n
          • float avg_vdd: the Vdd measured simultaneously with v_avg (\nnot used)
          • \n
          \n\n
          Returns
          \n\n
          \n

          \n
          \n\n

          Returns: P_avg, P_std, P_avg_std all in Torr

          \n", "signature": "(self, v_avg, v_std, avg_std, avg_vdd):", "funcdef": "def"}, {"fullname": "jupyterpidaq.Sensors.sensors.VernierGasP_OLD.mmHg", "modulename": "jupyterpidaq.Sensors.sensors", "qualname": "VernierGasP_OLD.mmHg", "kind": "function", "doc": "
          Parameters
          \n\n
            \n
          • float v_avg: average raw voltage
          • \n
          • float v_std: standard deviation of the voltage measurements
          • \n
          • float avg_std: estimate of the standard deviation of the average
          • \n
          • float avg_vdd: the Vdd measured simultaneously with v_avg (\nnot used)
          • \n
          \n\n
          Returns
          \n\n
          \n

          \n
          \n\n

          Returns: P_avg, P_std, P_avg_std all in mmHg

          \n", "signature": "(self, v_avg, v_std, avg_std, avg_vdd):", "funcdef": "def"}, {"fullname": "jupyterpidaq.Sensors.sensors.VernierGasP_OLD.atm", "modulename": "jupyterpidaq.Sensors.sensors", "qualname": "VernierGasP_OLD.atm", "kind": "function", "doc": "
          Parameters
          \n\n
            \n
          • float v_avg: average raw voltage
          • \n
          • float v_std: standard deviation of the voltage measurements
          • \n
          • float avg_std: estimate of the standard deviation of the average
          • \n
          • float avg_vdd: the Vdd measured simultaneously with v_avg (\nnot used)
          • \n
          \n\n
          Returns
          \n\n
          \n

          \n
          \n\n

          Returns: P_avg, P_std, P_avg_std all in atm

          \n", "signature": "(self, v_avg, v_std, avg_std, avg_vdd):", "funcdef": "def"}, {"fullname": "jupyterpidaq.Sensors.sensors.VernierpH", "modulename": "jupyterpidaq.Sensors.sensors", "qualname": "VernierpH", "kind": "class", "doc": "

          This class contains the definitions for Vernier standard pH\nsensor, PH-BTA.

          \n", "bases": "RawAtoD"}, {"fullname": "jupyterpidaq.Sensors.sensors.VernierpH.__init__", "modulename": "jupyterpidaq.Sensors.sensors", "qualname": "VernierpH.__init__", "kind": "function", "doc": "

          This init should be called first in the init section of any class\nextending this class (e.g. super().__init__(Vdd)). Then set\nself.name and self.vendor to the proper values. Append units\nspecific to the sensor to self.units. The parameter Vdd must be\nsupplied upon initialization because the output voltage of some\nsensors depends on Vdd.

          \n\n
          Parameters
          \n\n
            \n
          • float Vdd: the voltage supplied to the sensor by the A-to-D\nboard in case the sensor output depends on this.
          • \n
          \n", "signature": "(Vdd)"}, {"fullname": "jupyterpidaq.Sensors.sensors.VernierpH.pH", "modulename": "jupyterpidaq.Sensors.sensors", "qualname": "VernierpH.pH", "kind": "function", "doc": "
          Parameters
          \n\n
            \n
          • float v_avg: average raw voltage
          • \n
          • float v_std: standard deviation of the raw voltage
          • \n
          • float avg_std: estimated standard deviation of v_avg
          • \n
          • float avg_vdd: the Vdd measured simultaneously with v_avg (not\nused)
          • \n
          \n\n
          Returns
          \n\n
          \n

          \n
          \n\n

          Returns: pH_avg, pH_std, pH_avg_std all in pH units

          \n", "signature": "(self, v_avg, v_std, avg_std, avg_vdd):", "funcdef": "def"}, {"fullname": "jupyterpidaq.Sensors.sensors.VernierFlatpH", "modulename": "jupyterpidaq.Sensors.sensors", "qualname": "VernierFlatpH", "kind": "class", "doc": "

          This class contains the definitions for Vernier flat tris-compatible pH\nsensor, FPH-BTA.

          \n", "bases": "RawAtoD"}, {"fullname": "jupyterpidaq.Sensors.sensors.VernierFlatpH.__init__", "modulename": "jupyterpidaq.Sensors.sensors", "qualname": "VernierFlatpH.__init__", "kind": "function", "doc": "

          This init should be called first in the init section of any class\nextending this class (e.g. super().__init__(Vdd)). Then set\nself.name and self.vendor to the proper values. Append units\nspecific to the sensor to self.units. The parameter Vdd must be\nsupplied upon initialization because the output voltage of some\nsensors depends on Vdd.

          \n\n
          Parameters
          \n\n
            \n
          • float Vdd: the voltage supplied to the sensor by the A-to-D\nboard in case the sensor output depends on this.
          • \n
          \n", "signature": "(Vdd)"}, {"fullname": "jupyterpidaq.Sensors.sensors.VernierFlatpH.pH", "modulename": "jupyterpidaq.Sensors.sensors", "qualname": "VernierFlatpH.pH", "kind": "function", "doc": "
          Parameters
          \n\n
            \n
          • float v_avg: average raw voltage
          • \n
          • float v_std: standard deviation of the raw voltage
          • \n
          • float avg_std: estimated standard deviation of v_avg
          • \n
          • float avg_vdd: the Vdd measured simultaneously with v_avg (not\nused)
          • \n
          \n\n
          Returns
          \n\n
          \n

          \n
          \n\n

          Returns: pH_avg, pH_std, pH_avg_std all in pH units

          \n", "signature": "(self, v_avg, v_std, avg_std, avg_vdd):", "funcdef": "def"}]; // mirrored in build-search-index.js (part 1) // Also split on html tags. this is a cheap heuristic, but good enough. diff --git a/setup.py b/setup.py index a6ea72b..6ab74a3 100644 --- a/setup.py +++ b/setup.py @@ -6,7 +6,7 @@ setuptools.setup( name="JupyterPiDAQ", url = "https://github.com/JupyterPhysSciLab/JupyterPiDAQ", - version="0.8.0", + version="0.8.1", description="Live Data Acquisition in Jupyter notebooks", long_description=long_description, long_description_content_type="text/markdown",