Adquisición programada en Python

Otra ventaja de la conversión del mundo analógico al digital es la posibilidad de programar nuestro instrumental, dado que es esencialmente una computadora.

Programar significa dar órdenes para que la computadora ejecute ciertas órdenes en un determinado orden y una dada cantidad de veces. Pero para comunicarse con la computadora que hará las acciones desde una que nosotros manejamos necesitamos que entre ellas haya un "lenguaje común". Eso se llama protocolo.

En nuestro caso utilizaremos el instrumental Tektronix, y tenemos la suerte de que esta empresa haya desarrollado un protocolo de comunicación con las PC, llamado VISA (Virtual Instrument Software Architecture).

Según la empresa: "is an industry-standard communication protocol. VISA is a Test & Measurement industry standard communication API (Application Programming Interface) for use with test and measurement devices. Some times called a communication driver, VISA allows for the development of programs to be bus independent. Using VISA libraries enables communication for many interfaces such as GPIB, USB, and Ethernet."

Una vez establecida la comunicación entre las dos PC's, tenemos que ver cómo pedirle cosas al instrumental en algún lenguaje. Algo de eso ya hicimos, tenemos un software que permite hacer el screenshot del osciloscopio y toma los datos en pantalla, te los guarda en un txt. Lo que vamos a hacer ahora es superar al software "manual", y vamos a programarlo para que adquiera N veces, y nos guarde sólo un parámetro de la adquisición, y luego plotee ese parámetro en función de lo que determinemos. Con Python podemos hacer todo eso.

Generador de funciones

La biblioteca que utilizaremos para establecer la comunicación PC-instrumental se llama pyvisa. Con ella, deberemos crearnos un tipo de variable llamado Resource Manager, desde el cual le pediremos información al instrumental.

Probaremos esto para un generador de funciones Tektronix


In [ ]:
import time

import numpy as np
import visa

rm = visa.ResourceManager() # Creamos al Resource Manager

rm.list_resources() # Esto les permitirá ver qué es lo que pyvisa reconoce conectado a la PC

resource_name = 'USB0::0x0699::0x0346::C033250::INSTR' # Este es un nombre ejemplo con el cual Pyvisa reconoce al instrumento

fungen = rm.open_resource(resource_name) # "Abrimos la comunicación con el aparato llamándolo por su nombre

fungen.write('*IDN?') # Entre otras cosas, nosotros podemos preguntarle al generador de funciones quién es o cómo se hace llamar

print(fungen.read())

Es importante en los pasos que acabamos de dar que reconozcamos cuando Pyvisa reconoce nuestro instrumental y cuando no. Si nos conectamos por USB, eso debería indicarlo (distinto sería si nos conectáramos por puerto GPIB o similares), y si es un instrumento, debería decir "instr" o similar. Todos los demás números del nombre del recurso reconocen marca, modelo y número de serie, propio de cada aparato.

Notemos que la comunicación, el envío y la recepción de información, se hace por medio de un write y un read. En el primero, definimos qué string vamos a mandarle al aparato. Esos strings los sabemos si tenemos acceso a su manual de programación, y es propio de cada marca y a veces del modelo. En particular, el string IDN? lo que hace es solicitarle al aparato su "identidad", por lo que este generador de funciones emite y almacena en un buffer esa información, disponible si nosotros emitimos un read.

El equivalente a enviar un string y leer el buffer es la función query


In [ ]:
print(fungen.query('*IDN?'))

A parte de preguntarle cosas, al generador de funciones le puedo setear condiciones, como el voltaje, la frecuencia o el offset de la señal que quiero que genere:


In [ ]:
# Rampa logaritmica de frequencias 
# Los dos primeros numeros (1 y 3) indican los exponentes de los limites(10^1 y 10^3)
# El siguiente el numero de pasos
for freq in np.logspace(1, 3, 20):
    fungen.write('FREQ %f' % freq)
    time.sleep(0.1)

# Rampa lineal de amplitudes
# Los dos primeros numeros (0 y 1) indican los limites.
# El siguiente el numero de pasos
for amplitude in np.linspace(0, 1, 10):
    fungen.write('VOLT %f' % amplitude)
    time.sleep(0.1)
    
    
# Rampa lineal de offset
# Los dos primeros numeros (0 y 1) indican los limites.
# El siguiente el numero de pasos
for offset in np.linspace(0, 1, 10):
    fungen.write('VOLT:OFFS %f' % offset)
    time.sleep(0.1)

Notemos que la función write pretende un caracter como argumento para enviar al generador, lo cual coincide con lo que veniamos diciendo antes. Qué palabras y cómo escribirlas dependerá siempre del instrumental que usemos y de su manual.

Es importante que siempre que terminemos de hacer una medición, cerremos la comunicación con el aparato.


In [ ]:
fungen.close()

Osciloscopio

Con el osciloscopio será distinto, porque es más común leer datos que pedirle que haga cosas. Al igual que antes, iniciamos un Resource Manager y abrimos comunicación con el osciloscopio.


In [ ]:
rm = visa.ResourceManager()

rm.list_resources()

resource_name = 'USB0::0x0699::0x0363::C065089::INSTR'

osci = rm.open_resource(resource_name)

osci.query('*IDN?')

Bien, para lo siguiente es importante reconocer que los tipos de datos que el osciloscopio nos puede ofrecer se pueden escribir en ASCII o en binario. El ASCII es una forma de enumerar a todos los dígitos y teclas conocidas, es un estándar de representación numérica. El binario es lo que es, un número escrito en binario. Generalmente, el ASCII es más fácil de leer (aunque con cierta dificultad) por un humano, pero eso lo hace más difícil de manipular. En cambio información en binario es mucho más difícil de leer, pero mejor para cálculos.

Nosotros le pediremos al osciloscopio que nos escriba en binario


In [ ]:
osci.write('DAT:ENC RPB') # Recordar que esto puede depender del instrumental usado y de su sintaxis
osci.write('DAT:WID 1')

Luego, los datos del osciloscopio los necesitaremos calibrar. Para eso necesitamos ciertos parámetros:


In [ ]:
xze, xin, yze, ymu, yoff = osci.query_ascii_values('WFMPRE:XZE?;XIN?;YZE?;YMU?;YOFF?;', separator=';')

Luego sí, pedimos que levante la curva en pantalla, y la magia está cuando la ploteamos.


In [ ]:
data = osci.query_binary_values('CURV?', datatype='B', container=np.array)

tiempo = xze + np.arange(len(data)) * xin

plt.plot(tiempo, data)

Y no olvidemos cerrar la comunicación con el aparato


In [ ]:
fungen.close()

Para más información...

dirigirse a ventanilla de ventas. Muchas gracias.

En este link podrán acceder a más ejemplos con pyvisa, gentileza de Hernán Grecco.