<SYSTEM_TASK:>
Given the following text description, write Python code to implement the functionality described below step by step
<END_TASK>
<USER_TASK:>
Description:
 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
Step1:  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
Step2:  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
Step3:  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.
Step4:  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.
Step5:  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
Step6:  Luego, los datos del osciloscopio los necesitaremos calibrar. Para eso necesitamos ciertos parámetros
Step7:  Luego sí, pedimos que levante la curva en pantalla, y la magia está cuando la ploteamos.
Step8:  Y no olvidemos cerrar la comunicación con el aparato
<ASSISTANT_TASK:>
Python Code:
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())
Explanation: 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
End of explanation
print(fungen.query('*IDN?'))
Explanation: 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
End of explanation
# 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)
Explanation: 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:
End of explanation
fungen.close()
Explanation: 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.
End of explanation
rm = visa.ResourceManager()
rm.list_resources()
resource_name = 'USB0::0x0699::0x0363::C065089::INSTR'
osci = rm.open_resource(resource_name)
osci.query('*IDN?')
Explanation: 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.
End of explanation
osci.write('DAT:ENC RPB') # Recordar que esto puede depender del instrumental usado y de su sintaxis
osci.write('DAT:WID 1')
Explanation: 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
End of explanation
xze, xin, yze, ymu, yoff = osci.query_ascii_values('WFMPRE:XZE?;XIN?;YZE?;YMU?;YOFF?;', separator=';')
Explanation: Luego, los datos del osciloscopio los necesitaremos calibrar. Para eso necesitamos ciertos parámetros:
End of explanation
data = osci.query_binary_values('CURV?', datatype='B', container=np.array)
tiempo = xze + np.arange(len(data)) * xin
plt.plot(tiempo, data)
Explanation: Luego sí, pedimos que levante la curva en pantalla, y la magia está cuando la ploteamos.
End of explanation
fungen.close()
Explanation: Y no olvidemos cerrar la comunicación con el aparato
End of explanation
<END_TASK>