Electrónica Analógica · Tema 07

Señales en Python

Osciloscopio de código puro: generar ondas, medir estadísticas, estimar frecuencia y graficar en texto.

01 · Punto de partida

Sin osciloscopio, con intérprete

Generar una senoide de 50 Hz en código cuesta 3 líneas; medirle medio, RMS y frecuencia, otras diez; graficarla en texto, quince. Ese toolkit (solo biblioteca estándar, sin numpy) te acompaña todo el curso: predecir filtros, verificar comparadores y entender muestreo antes de tocar hardware.

Al terminar, cualquier señal del curso existe primero como lista de Python.

  • ¿Cómo genero las 4 formas con parámetros?
  • ¿Cómo estimo f sin saberla?
  • ¿Cómo grafico sin librerías?
  • ¿Qué pasa con ruido y pocas muestras?

02 · Generar

Cuatro formas, una función

Parámetros: amplitud, frecuencia, offset, duty, muestras por segundo (fs) y segundos. Regla de oro: fs ≥ 20×f para verla linda (Nyquist pide 2×, el ojo pide 20×).

Python en tu navegador. Generá tus ondas y ejecutá el análisis.

import math

def generar(forma="seno", amp=5.0, f_hz=50.0, offset=0.0, duty=0.5, fs=2000.0, segundos=0.1):
    n = int(fs * segundos)
    vals = []
    for k in range(n):
        t = k / fs
        f = (t * f_hz) % 1.0  # fase 0..1
        if forma == "seno":
            v = amp * math.sin(2 * math.pi * f)
        elif forma == "cuadrada":
            v = amp if f < duty else -amp
        elif forma == "triangular":
            v = amp * (4 * abs(f - 0.5) - 1)
        else:  # diente
            v = amp * (2 * f - 1)
        vals.append(v + offset)
    return vals, fs


onda, fs = generar("seno", amp=5.0, f_hz=50.0)
print(f"{len(onda)} muestras a {fs} Hz: max={max(onda):.1f} min={min(onda):.1f}")

Generá cuadrada 0–5 (amp 2.5, offset 2.5, duty 0.25): ¿max/min? ¿Y triangular ±3?

03 · Medir

Estadísticas + frecuencia por cruces

f estimada = cruces ascendentes por el medio / segundos. Con fs holgada y sin ruido, clava el valor real.

def analizar(vals, fs):
    medio = sum(vals) / len(vals)
    rms = math.sqrt(sum(v * v for v in vals) / len(vals))
    cruces = sum(1 for a, b in zip(vals, vals[1:]) if a < medio <= b)
    f_est = cruces / (len(vals) / fs)
    print(f"medio={medio:.2f}V rms={rms:.2f}V cruces={cruces} f~={f_est:.1f}Hz")
    return medio, rms, f_est


onda, fs = generar("seno", amp=5.0, f_hz=50.0)
analizar(onda, fs)
onda2, _ = generar("cuadrada", amp=2.5, offset=2.5, duty=0.25, f_hz=100.0, fs=5000.0, segundos=0.1)
analizar(onda2, 5000.0)

La cuadrada debe dar medio 1,25 y f ≈ 100. Si tu fs fuera 150 Hz, ¿qué estimaría? (Pista: aliasing.)

04 · Graficar

ASCII plot sin librerías

Quince líneas que muestran la forma en la consola: escala al rango y pinta una fila por muestra.

def ascii_plot(vals, ancho=60, alto=12):
    lo, hi = min(vals), max(vals)
    span = (hi - lo) or 1.0
    paso = max(1, len(vals) // ancho)
    for fila in range(alto):
        nivel = hi - (fila + 0.5) / alto * span
        linea = "".join("#" if v >= nivel else " " for v in vals[::paso])
        print(f"{nivel:+6.1f}|{linea}")


onda, _ = generar("seno", amp=5.0, f_hz=50.0, fs=2000.0, segundos=0.06)
ascii_plot(onda)
onda2, _ = generar("cuadrada", amp=2.5, offset=2.5, duty=0.25, f_hz=50.0, fs=2000.0, segundos=0.06)
ascii_plot(onda2)

05 · Ruido

Ensuciar a propósito

Sumar gaussiano simula la mesa real: el promedio se salva, los cruces se multiplican. Lección: estimar f con histéresis o filtrando (Temas 20 y 23), no con cruces crudos.

import random

random.seed(7)

def con_ruido(vals, sigma=0.3):
    return [v + random.gauss(0, sigma) for v in vals]


onda, fs = generar("seno", amp=5.0, f_hz=50.0)
limpia = analizar(onda, fs)
sucia = analizar(con_ruido(onda), fs)
print("Compará: medio y rms casi iguales, cruces disparados")

Sin ruido

Cruces exactos

f estimada clava 50 Hz: cada cruce es real y cuenta.

Con ruido

Cruces falsos

El ruido cruza y recruza el medio: f estimada se dispara. Filtrar o histéresis (Temas 20, 23).

06 · Analogía programador

Observabilidad de series

  1. 1
    Generar = fixtures.

    Datos sintéticos con forma conocida para probar el análisis antes que el hardware.

  2. 2
    Cruces = eventos.

    Detectar flancos como webhooks: contar transiciones en vez de promediar niveles.

  3. 3
    ASCII = logging.

    Ver sin GUI: printf-debugging de señales, suficiente el 80 % de las veces.

07 · Exploración

Laboratorio: parámetros en vivo

Elegí forma y mové amplitud, frecuencia y duty: el laboratorio calcula medio, RMS, cruces y f estimada con fs fija de 2 kHz.

LABORATORIO 07

Generador + análisis

fs = 2000 Hz fijos

Medio / RMS0,00 / 3,54 V
f estimada50 Hz ✓
Muestras/ciclo40 (sobrado)
VeredictoLinda para analizar

Senoide 5 V a 50 Hz con fs 2 kHz.

Preguntas para explorar

  1. Subí a 900 Hz. ¿Muestras/ciclo? ¿Sigue linda la estimación?
  2. ¿Por qué la triangular da el mismo medio que la senoide pero otro RMS?
  3. ¿Qué pasa si pedís f mayor que fs/2? (Probá mentalmente 1500 Hz.)
Ver respuestas sugeridas
  1. 2,2 por ciclo: al borde (Nyquist pide > 2). La estimación tiembla: fs 2 kHz no alcanza para 900 Hz con holgura.
  2. Medio 0 ambas por simetría; RMS 2,89 vs 3,54 por forma (Tema 6). La forma decide la energía.
  3. Aliasing: 1500 Hz con fs 2000 se ve como 500 Hz falsa. Por eso fs ≥ 20×f para trabajar tranquilo.

08 · Comprensión

Confusiones frecuentes

«Más muestras siempre mejor»

Mejor hasta sobrar (20×f): más allá solo gastan CPU sin información nueva. Muestrear de más no arregla ruido ni aliasing.

«Cruces = frecuencia siempre»

Solo sin ruido y con fs holgada. Con ruido u offset descentrado, los cruces mienten: verificar forma primero (ASCII).

«Random sin semilla da igual»

Sin seed cada corrida difiere y los experimentos no se repiten. Fijar semilla = experimento reproducible.

«ASCII no sirve para nada serio»

Sirve para el 80 %: offset, recorte, duty y ruido se ven en texto. El 20 % fino pide graficador real (curso 4+).

09 · Práctica guiada

Ejercicios de toolkit

Ejercicio 1: replicar el lab

Generá senoide ±5 V a 50 Hz con fs 2000 y ejecutá analizar(). ¿Medio/RMS/f? Compará con el laboratorio.

Ver solución razonada

Medio ≈ 0, RMS ≈ 3,54, f ≈ 50 Hz. Código y laboratorio deben coincidir: misma matemática.

Ejercicio 2: cazar aliasing

Generá senoide 1500 Hz con fs 2000 y analizá. ¿Qué f estima? Explicá con Nyquist.

Ver solución

Estima ≈ 500 Hz (2000 − 1500: reflejo en fs/2). Alias clásico: fs < 2f miente con confianza.

Ejercicio 3: duty a ojo y a número

Graficá cuadrada 0–5 duty 25 % con ascii_plot() y medí con analizar(). ¿Coinciden ojo y número?

Ver solución

ASCII muestra 1/4 en alto; análisis da medio 1,25 y f exacta. Ojo para forma, número para valor.

10 · Síntesis

Ideas para recordar

  • Generar con parámetros (A, f, offset, duty, fs ≥ 20f).
  • Medir: medio, RMS y f por cruces ascendentes.
  • Graficar en ASCII antes de creer estadísticas.
  • Ruido rompe cruces, no promedios: filtrar o histéresis.
  • Seed fija = experimento repetible.

Toolkit listo para todo el curso. Ahora, a medir de verdad: en el próximo tema, medir señales con tester y osciloscopio.