diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 7501d059267552b510b5eb1e2a6529b57428e592..6e1207196a145cf6525fbe61067b13e19bd9cb53 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -6,15 +6,16 @@ pages: script: - apt-get update - apt-get install --assume-yes pandoc - - pip install sphinx numpydoc sphinx_rtd_theme sphinx_nbexamples pandoc + - pip install numpy pandas termcolor # top import of Ohmpi.py + - pip install sphinx numpydoc sphinx_rtd_theme pandoc recommonmark - cd doc - make html # also make latex? pdf? - cd .. - - mv doc/_build_html/ public/ + - mv doc/build/html/ public/ - ls public/ artifacts: paths: - public/ only: - - master \ No newline at end of file + - master diff --git a/Ohmpi.py b/Ohmpi.py index c7302d1f9a68cc2ad09940988639307e2adf21e7..81ca7339364e9487f2e4e1f813a860fea3357c58 100644 --- a/Ohmpi.py +++ b/Ohmpi.py @@ -1,7 +1,9 @@ +# -*- coding: utf-8 -*- """ created on January 6, 2020 Update February 2022 -Ohmpi.py is a program to control a low-cost and open hardward resistivity meter OhmPi that has been developed by Rémi CLEMENT(INRAE),Vivien DUBOIS(INRAE),Hélène GUYARD(IGE), Nicolas FORQUET (INRAE), and Yannick FARGIER (IFSTTAR). +Ohmpi.py is a program to control a low-cost and open hardward resistivity meter OhmPi that has been developed by Rémi CLEMENT (INRAE),Vivien DUBOIS (INRAE),Hélène GUYARD (IGE), Nicolas FORQUET (INRAE), Yannick FARGIER (IFSTTAR) +and Guillaume BLANCHY (ILVO). """ VERSION = '2.0.0' @@ -28,16 +30,6 @@ from datetime import datetime from termcolor import colored import threading -if True: - import board, busio, adafruit_tca9548a - import adafruit_ads1x15.ads1115 as ADS - from adafruit_ads1x15.analog_in import AnalogIn - from adafruit_mcp230xx.mcp23008 import MCP23008 - from adafruit_mcp230xx.mcp23017 import MCP23017 - import digitalio - from digitalio import Direction - from gpiozero import CPUTemperature - current_time = datetime.now() print(current_time.strftime("%Y-%m-%d %H:%M:%S")) @@ -50,22 +42,22 @@ print(current_time.strftime("%Y-%m-%d %H:%M:%S")) class OhmPi(object): + """Create the main OhmPi object. + + Parameters + ---------- + config : str, optional + Path to the .json configuration file. + sequence : str, optional + Path to the .txt where the sequence is read. By default, a 1 quadrupole + sequence: 1, 2, 3, 4 is used. + onpi : bool, optional + True if running on the RaspberryPi. False for testing (random data generated). + output : str, optional + Either 'print' for a console output or 'mqtt' for publication onto + MQTT broker. + """ def __init__(self, config=None, sequence=None, onpi=True, output='print'): - """Create the main OhmPi object. - - Parameters - ---------- - config : str, optional - Path to the .json configuration file. - sequence : str, optional - Path to the .txt where the sequence is read. By default, a 1 quadrupole - sequence: 1, 2, 3, 4 is used. - onpi : bool, optional - True if running on the RaspberryPi. False for testing (random data generated). - output : str, optional - Either 'print' for a console output or 'mqtt' for publication onto - MQTT broker. - """ # flags and attributes self.onpi = onpi # True if run from the RaspberryPi with the hardware, otherwise False for random data self.output = output # type of output print @@ -73,6 +65,18 @@ class OhmPi(object): self.run = False # flag is True when measuring self.thread = None # contains the handle for the thread taking the measurement self.path = 'data/' # wher to save the .csv + + # finish import (done only when class is instantiated as some libs are + # only available on arm64 platform) + if self.onpi: + import board, busio, adafruit_tca9548a + import adafruit_ads1x15.ads1115 as ADS + from adafruit_ads1x15.analog_in import AnalogIn + from adafruit_mcp230xx.mcp23008 import MCP23008 + from adafruit_mcp230xx.mcp23017 import MCP23017 + import digitalio + from digitalio import Direction + from gpiozero import CPUTemperature # read in hardware parameters (seetings.py) self._read_hardware_parameters() @@ -590,8 +594,8 @@ class OhmPi(object): self.dump('status = ' + self.status) # test -ohmpi = OhmPi(config='ohmpi_param.json') -ohmpi.measure() -time.sleep(4) -ohmpi.stop() +#ohmpi = OhmPi(config='ohmpi_param.json') +#ohmpi.measure() +#time.sleep(4) +#ohmpi.stop() diff --git a/Ohmpi_4elec.py b/Ohmpi_4elec.py deleted file mode 100644 index ce3b060c1a32fa3b78366cd634655f0d701e05fe..0000000000000000000000000000000000000000 --- a/Ohmpi_4elec.py +++ /dev/null @@ -1,285 +0,0 @@ -""" -created on september , 2021 -Update september 2021 -Ohmpi_4elec.py is a program to control a low-cost and open hardward resistivity meter OhmPi that has been developed by Rémi CLEMENT(INRAE),Vivien DUBOIS(INRAE),Hélène GUYARD(IGE), Nicolas FORQUET (INRAE), Oliver KAUFMANN (UMONS) and Yannick FARGIER (IFSTTAR). -""" - -print('\033[1m'+'\033[31m'+' ________________________________') -print('| _ | | | || \/ || ___ \_ _|') -print('| | | | |_| || . . || |_/ / | |' ) -print('| | | | _ || |\/| || __/ | |') -print('\ \_/ / | | || | | || | _| |_') -print(' \___/\_| |_/\_| |_/\_| \___/ ') -print('\033[0m') -print('OhmPi 4 elec start' ) -print('Vers: 1.01') -print('Import library') - - - -import time , board, busio, numpy, os, sys, json, glob,os.path,adafruit_tca9548a -import adafruit_ads1x15.ads1115 as ADS -from adafruit_ads1x15.analog_in import AnalogIn -from pandas import DataFrame -from datetime import datetime -from adafruit_mcp230xx.mcp23008 import MCP23008 -from adafruit_mcp230xx.mcp23017 import MCP23017 -from termcolor import colored -import digitalio -from digitalio import Direction -from gpiozero import CPUTemperature -current_time = datetime.now() -print(current_time.strftime("%Y-%m-%d %H:%M:%S")) -""" -hardware parameters -""" -R_shunt = 2# reference resistance value in ohm -Imax=4800/50/2 -print(colored(('The maximum current cannot be higher than ',Imax, 'mA'), 'red')) - -coef_p2 = 2.50# slope for current conversion for ADS.P2, measurement in V/V -coef_p3 = 2.50 # slope for current conversion for ADS.P3, measurement in V/V -offset_p2= 0 -offset_p3= 0 -integer=2 #Max value 10 -meas=numpy.zeros((3,integer)) - -""" -import parameters -""" - -with open('ohmpi_param.json') as json_file: - pardict = json.load(json_file) - - -i2c = busio.I2C(board.SCL, board.SDA) #activation du protocle I2C -mcp = MCP23008(i2c, address=0x20) #connexion I2C MCP23008, injection de courant -ads_current = ADS.ADS1115(i2c, gain=2/3,data_rate=128, address=0X48)# connexion ADS1115, pour la mesure de courant -ads_voltage = ADS.ADS1115(i2c, gain=2/3,data_rate=128, address=0X49)# connexion ADS1115, pour la mesure de voltage -#initialisation desvoie pour la polarité -pin0 = mcp.get_pin(0) -pin0.direction = Direction.OUTPUT -pin1 = mcp.get_pin(1) -pin1.direction = Direction.OUTPUT -pin0.value = False -pin1.value = False -""" -functions -""" -# function swtich_mux select the right channels for the multiplexer cascade for electrodes A, B, M and N. -def switch_mux_on(quadripole): - elec_adress=[0x76,0X71,0x74,0x70] - - for i in range(0,4): - tca= adafruit_tca9548a.TCA9548A(i2c, elec_adress[i]) #choose MUX A B M or N - - if quadripole[i] < 17: - nb_i2C=7 - a=quadripole[i] - elif quadripole[i] > 16 and quadripole[i] < 33: - nb_i2C=6 - a=quadripole[i]-16 - elif quadripole[i] > 32 and quadripole[i] < 49: - nb_i2C=5 - a=quadripole[i]-32 - elif quadripole[i] > 48 and quadripole[i] < 65: - nb_i2C=4 - a=quadripole[i]-48 - - mcp2 = MCP23017(tca[nb_i2C]) - mcp2.get_pin(a-1).direction=digitalio.Direction.OUTPUT - mcp2.get_pin(a-1).value=True - -def switch_mux_off(quadripole): - elec_adress=[0x76,0X71,0x74,0x70] - - for i in range(0,4): - tca= adafruit_tca9548a.TCA9548A(i2c, elec_adress[i]) #choose MUX A B M or N - - if quadripole[i] < 17: - nb_i2C=7 - a=quadripole[i] - elif quadripole[i] > 16 and quadripole[i] < 33: - nb_i2C=6 - a=quadripole[i]-16 - elif quadripole[i] > 32 and quadripole[i] < 49: - nb_i2C=5 - a=quadripole[i]-32 - elif quadripole[i] > 48 and quadripole[i] < 65: - nb_i2C=4 - a=quadripole[i]-48 - - mcp2 = MCP23017(tca[nb_i2C]) - mcp2.get_pin(a-1).direction=digitalio.Direction.OUTPUT - mcp2.get_pin(a-1).value=False - - -#function to switch off mux -def ZERO_mux(nb_elec): - elec_adress=[0x76,0X71,0x74,0x70] - for i in range(0,4): - tca= adafruit_tca9548a.TCA9548A(i2c, elec_adress[i]) #choose MUX A B M or N - for y in range(0,nb_elec): - qd=y+1 - if qd < 17: - nb_i2C=7 - a=qd - elif qd > 16 and qd < 33: - nb_i2C=6 - a=qd-16 - elif qd > 32 and qd < 49: - nb_i2C=5 - a=qd-32 - elif qd > 48 and qd < 65: - nb_i2C=4 - a=qd-48 - - mcp2 = MCP23017(tca[nb_i2C]) - mcp2.get_pin(a-1).direction=digitalio.Direction.OUTPUT - mcp2.get_pin(a-1).value= False - -# function to find rows with identical values in different columns -def find_identical_in_line(array_object): - output = [] - if array_object.ndim == 1: - temp = numpy.zeros(4) - for i in range(len(array_object)): - temp[i] = numpy.count_nonzero(array_object == array_object[i]) - if any(temp > 1): - output.append(0) - else: - for i in range(len(array_object[:,1])): - temp = numpy.zeros(len(array_object[1,:])) - for j in range(len(array_object[1,:])): - temp[j] = numpy.count_nonzero(array_object[i,:] == array_object[i,j]) - if any(temp > 1): - output.append(i) - return output -# -# read quadripole file and apply tests -def read_quad(filename, nb_elec): - output = numpy.loadtxt(filename, delimiter=" ",dtype=int) # load quadripole file - # locate lines where the electrode index exceeds the maximum number of electrodes - test_index_elec = numpy.array(numpy.where(output > nb_elec)) - # locate lines where an electrode is referred twice - test_same_elec = find_identical_in_line(output) - # if statement with exit cases (rajouter un else if pour le deuxième cas du ticket #2) - if test_index_elec.size != 0: - for i in range(len(test_index_elec[0,:])): - print("Error: An electrode index at line "+ str(test_index_elec[0,i]+1)+" exceeds the maximum number of electrodes") - sys.exit(1) - elif len(test_same_elec) != 0: - for i in range(len(test_same_elec)): - print("Error: An electrode index is used twice at line " + str(test_same_elec[i]+1)) - sys.exit(1) - else: - return output - - -def run_measurement(nb_stack, injection_deltat, R_shunt, coefp2, coefp3): - start_time=time.time() - # inner variable initialization - sum_I=0 - sum_Vmn=0 - sum_Ps=0 - # injection courant and measure - mcp = MCP23008(i2c, address=0x20) - pin0 = mcp.get_pin(0) - pin0.direction = Direction.OUTPUT - pin1 = mcp.get_pin(1) - pin1.direction = Direction.OUTPUT - pin0.value = False - pin1.value = False - for n in range(0,3+2*nb_stack-1) : - # current injection - - if (n % 2) == 0: - - pin1.value = True - pin0.value = False # current injection polarity n°1 - else: - pin0.value = True - pin1.value = False# injection de courant polarity n°2 - start_delay=time.time()#stating measurement time - time.sleep(injection_deltat) # delay depending on current injection duration - -# mesureament of i and u - for k in range(0,integer): - meas[0,k] = ((AnalogIn(ads_current,ADS.P0).voltage/50)/R_shunt)*1000 # reading current value on ADS channel A0 - meas[1,k] = AnalogIn(ads_voltage,ADS.P0).voltage * coefp2*1000 - meas[2,k] = AnalogIn(ads_voltage,ADS.P1).voltage * coefp3*1000 # reading voltage value on ADS channel A2 - pin1.value = False; pin0.value = False# stop current injection - end_delay=time.time() - sum_I=sum_I+(numpy.mean(meas[0,:])) - Vmn1=((numpy.mean(meas[1,:]))-(numpy.mean(meas[2,:]))) - if (n % 2) == 0: - sum_Vmn=sum_Vmn-Vmn1 - sum_Ps=sum_Ps+Vmn1 - else: - sum_Vmn=sum_Vmn+Vmn1 - sum_Ps=sum_Ps+Vmn1 - - cpu = CPUTemperature() - - end_calc=time.time() - time.sleep(2*(end_delay-start_delay)-(end_calc-start_delay)) - #end_sleep2=time.time() - #print(['sleep=',((end_sleep2-start_delay))]) - - #print(['true delta=',((end_delay-start_delay)-injection_deltat)]) - #print(['time stop=',((2*(end_delay-start_delay)-(end_calc-start_delay)))]) - # return averaged values -# cpu= CPUTemperature() - output = DataFrame({ - "time":[datetime.now()], - "A":[(1)], - "B":[(2)], - "M":[(3)], - "N":[(4)], - "inj time [ms]" : (end_delay-start_delay)*1000, - "Vmn [mV]":[(sum_Vmn/(3+2*nb_stack-1))], - "I [mA]":[(sum_I/(3+2*nb_stack-1))], - "R [ohm]":[( (sum_Vmn/(3+2*nb_stack-1)/(sum_I/(3+2*nb_stack-1))))], - "Ps [mV]":[(sum_Ps/(3+2*nb_stack-1))], - "nbStack":[nb_stack], - "CPU temp [°C]":[cpu.temperature], - "Time [S]":[(-start_time+time.time())], - "Integer [-]": [integer] - - - - # Dead time equivalent to the duration of the current injection pulse - }) - output=output.round(2) - print(output.to_string()) - time.sleep(1) - return output - -# save data -def append_and_save(path, last_measurement): - - if os.path.isfile(path): - # Load data file and append data to it - with open(path, 'a') as f: - last_measurement.to_csv(f, header=False) - else: - # create data file and add headers - with open(path, 'a') as f: - last_measurement.to_csv(f, header=True) - - -""" -Main loop -""" -for g in range(0,pardict.get("nbr_meas")): # for time-lapse monitoring - - - - - current_measurement = run_measurement(pardict.get("stack"), pardict.get("injection_duration"), R_shunt, coef_p2, coef_p3) - - append_and_save(pardict.get("export_path"), current_measurement) - - - - time.sleep(pardict.get("sequence_delay")) #waiting next measurement (time-lapse) diff --git a/doc/docker.sh b/doc/docker.sh new file mode 100644 index 0000000000000000000000000000000000000000..ba1ccdee08c7b8c407031638c1ae9db692fd28e9 --- /dev/null +++ b/doc/docker.sh @@ -0,0 +1,4 @@ +# to test documentation build using same environemnet as gitlab doc runner + +docker run -it -v /media/jkl/data/ilvo/ohmpi/OhmP:/OhmPi python:3.9-bullseye bash + diff --git a/doc/running_html.py b/doc/running_html.py deleted file mode 100644 index 27194cada6965c089aea87a7e4dbdf46e3bb473b..0000000000000000000000000000000000000000 --- a/doc/running_html.py +++ /dev/null @@ -1,9 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Created on Mon Jul 13 21:48:00 2020 - -@author: remi.clement -""" - -import shutil - diff --git a/doc/source/Ohmpi.rst b/doc/source/Ohmpi.rst index 9eb52792be48c3ef115bcbf4ee0caaa0740d2595..5a3cab23f61497ada19f9b7bceedf3980a3155ad 100644 --- a/doc/source/Ohmpi.rst +++ b/doc/source/Ohmpi.rst @@ -13,12 +13,12 @@ OhmPi project **Authors:** ------------ -| Rémi CLEMENT,Vivien DUBOIS,Nicolas Forquet, INRAE, REVERSAAL, Villeurbanne, France. -| Yannick FARGIER, GERS-RRO, Univ Gustave Eiffel, IFSTTAR, Lyon, France. -| Julien GANCE, IRIS Instruments, Orléans, France. -| Hélène GUYARD, IGE Grenoble, Université Grenoble Alpes, Grenoble, France. +| Rémi CLEMENT,Vivien DUBOIS,Nicolas Forquet, INRAE, REVERSAAL, Villeurbanne, France +| Yannick FARGIER, GERS-RRO, Univ Gustave Eiffel, IFSTTAR, Lyon, France +| Julien GANCE, IRIS Instruments, Orléans, France +| Hélène GUYARD, IGE Grenoble, Université Grenoble Alpes, Grenoble, France | Olivier Kaufmann, Université de Mons, Mons, Belgium -| Guillaume Banchy, ILVO,Merelbeke, Belgium +| Guillaume Banchy, ILVO, Merelbeke, Belgium | @@ -62,4 +62,4 @@ to the near surface scientific community. .. note:: - Everyone willing to get involved is welcome in OhmPi Project!. \ No newline at end of file + Everyone willing to get involved is welcome in OhmPi Project!. diff --git a/doc/source/api.rst b/doc/source/api.rst new file mode 100644 index 0000000000000000000000000000000000000000..c1157b20f6f7090299118d1b70501ae0202d961a --- /dev/null +++ b/doc/source/api.rst @@ -0,0 +1,9 @@ +API reference +============= +.. toctree:: + :maxdepth: 2 + :caption: Contents: +.. automodule:: Ohmpi + :members: + + diff --git a/doc/source/conf.py b/doc/source/conf.py index 803d3705b28c413470cba85220848998b722eb16..d40917b39b5b6356a9a2ea5929b02072cf67942d 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -1,3 +1,5 @@ +# -*- coding: utf-8 -*- + # Configuration file for the Sphinx documentation builder. # # This file only contains a selection of the most common options. For a full @@ -12,15 +14,16 @@ # import os import sys -# sys.path.insert(0, os.path.abspath('.')) +sys.path.append(os.path.abspath('../..')) +import Ohmpi # import Ohmpi module to be documented in api.rst by numpydoc import sphinx_rtd_theme from shutil import copyfile # -- Project information ----------------------------------------------------- -project = 'Ohmpi:' -copyright = '2022, INRAE, Team Ohmpi' +project = 'OhmPi' +copyright = '2022, INRAE, Team OhmPi' author = 'Rémi CLEMENT' # The full version, including alpha/beta/rc tags @@ -30,8 +33,17 @@ release = 'open hardware resistivity-meter' # -- General configuration --------------------------------------------------- -extensions = ['recommonmark'] +extensions = [ + 'sphinx.ext.autodoc', + 'sphinx.ext.coverage', + 'sphinx.ext.mathjax', + 'numpydoc', + 'recommonmark' +] + + +numpydoc_show_class_members = False # Add any paths that contain templates here, relative to this directory. diff --git a/doc/source/index.rst b/doc/source/index.rst index 7ae7b05dc7509c2e505a4953a7b9efc74767d368..ccbf3e772af1caef76011d907450b62688bddd5a 100644 --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -37,6 +37,7 @@ Contents: V1_01 V1_02 V2_00 + api