Práctica 1: Señales, correlación y convolución
Señales y Sistemas Lineales
1 Introducción
En esta práctica ponemos a trabajar con MATLAB las operaciones básicas sobre señales ya vistas en teoría: la correlación, la convolución y la autocorrelación. En las dos primeras comparamos antes el cálculo del ordenador con un resultado teórico conocido, y después aplicamos la misma herramienta a una señal real o con ruido, donde el papel ya no llega: un radar que detecta objetos invisibles en la señal recibida y un efecto eco sobre una grabación de voz. Con la autocorrelación comparamos señales muy distintas (un pulso, una senoidal, ruido blanco y un código Barker), y cerramos volviendo al radar para ver por qué ese código separa dos objetos que el pulso senoidal confundía.
Damos por supuesta la Práctica 0, así que manejas vectores, operaciones elemento a elemento, funciones propias en archivos .m y los comandos básicos de representación gráfica.
Como en el resto de prácticas, el documento está pensado para que lo sigas escribiendo tú mismo el código en un script: no basta con leerlo, tienes que reproducir cada ejercicio y experimentar cambiando valores.
1.1 Objetivos
Al terminar deberías ser capaz de:
- Construir en MATLAB señales discretas (un vector de muestras, dibujado con
stem) y señales continuas (un vector de tiempos muy denso, dibujado conplot), incluidas las definidas a trozos. - Calcular la convolución de dos señales con
convy construir a mano el vector de índices del resultado, que MATLAB no proporciona. - Calcular la correlación cruzada como una convolución con una de las señales dada la vuelta (
fliplr), e interpretar la posición de su máximo como un retardo. - Modelar un efecto eco como la convolución con una respuesta impulsional formada por deltas.
- Reconocer las propiedades de la autocorrelación y explicar por qué la del ruido blanco es prácticamente una delta.
- Justificar, a partir de su autocorrelación, por qué un código Barker mejora la resolución de un radar frente a un pulso senoidal de la misma duración.
1.2 Estructura de la práctica
El documento se divide en dos partes:
- El trabajo previo debes prepararlo antes de acudir al laboratorio. Repasa cómo se representan las señales en MATLAB y las herramientas que usarás en la sesión. Cada ejercicio va acompañado de su solución, que puedes desplegar para comprobar tu respuesta. Intenta resolverlos por tu cuenta antes de mirarla.
- El trabajo de laboratorio es lo que realizarás durante la sesión, y aplica esas herramientas a problemas concretos. También aquí cada ejercicio trae una solución desplegable con el resultado que debes obtener. Si llegas con el previo hecho, la sesión te cundirá mucho más.
Es altamente recomendable trabajar en una carpeta dedicada solo para esta práctica y crear un archivo ejercicioX.m individual por cada ejercicio. En esa misma carpeta debes guardar los dos ficheros que se proporcionan para el trabajo de laboratorio, para que MATLAB pueda encontrarlos:
senyal_radar.m: genera la señal recibida por el radar.datos_voz.mat: la grabación de voz del efecto eco.
Recuerda completar el cuestionario en Aula Virtual antes de acudir al laboratorio, cuando termines el trabajo previo.
2 Trabajo previo
2.1 Senoidales discretas
Una senoidal discreta con \(N_0\) muestras por periodo es \[x[n] = \cos\left(\frac{2\pi}{N_0}\, n\right)\] y se repite cada \(N_0\) muestras: \(x[n + N_0] = x[n]\).
Un periodo completo son las muestras \(n = 0, 1, \dots, N_0 - 1\), es decir, \(N_0\) valores. La muestra \(n = N_0\) ya no pertenece a ese periodo sino que es la primera del siguiente. Por tanto, \(\mathbf{P}\) periodos completos tienen \(\mathbf{P \cdot N_0}\) muestras.
En MATLAB, generamos los índices de esta forma:
n = 0 : P*N0 - 1; % correcto: P*N0 muestras
n = 0 : P*N0; % incorrecto: P*N0 + 1 muestras (sobra una)2.2 Señales continuas
Un ordenador solo puede guardar una lista finita de números. Por eso en MATLAB toda señal es un vector, también las continuas. Una señal continua \(x(t)\) se representa por sus valores en una rejilla de instantes muy próximos entre sí:
N = 1000; % número de puntos
t = linspace(0, 2, N); % N instantes equiespaciados entre 0 y 2 s
x = cos(2*pi*t); % x(t) = cos(2πt) evaluada en esos instantes
plot(t, x) % plot une los puntos con líneasTécnicamente, el vector x es una señal discreta, pero si el número de puntos \(N\) es suficientemente grande, los puntos están tan juntos que plot, al unirlos con líneas, dibuja una curva indistinguible de la señal continua.
Diferencias de uso respecto a las señales discretas:
| Señal discreta \(x[n]\) | Señal continua \(x(t)\) | |
|---|---|---|
| Eje horizontal | n = 0 : N-1 |
t = linspace(t_ini, t_fin, N) |
| Cada elemento | es una muestra de la señal | es un punto de la aproximación |
| Representación | stem |
plot |
| Número de puntos | lo fija la propia señal | lo elegimos “suficientemente grande” |
Toda señal en MATLAB es un vector y por tanto tiene un número finito de puntos. Cuando hablamos de señales continuas, nos referimos a que el número de puntos es suficientemente alto como para que la curva dibujada por plot sea indistinguible de la señal continua.
2.3 Señales continuas a trozos
Todas las señales se construyen sobre un eje temporal como el del apartado anterior. En los siguientes ejemplos vamos a usar este vector de tiempos:
N = 1000;
t = linspace(-1, 6, N); % t en [-1, 6] sLa herramienta clave para dibujar señales continuas a trozos son las condiciones lógicas. El método es siempre el mismo:
- Empezar con un vector de ceros del tamaño de
t. - Para cada tramo, definir la condición que lo delimita.
- Asignar el valor de la señal en ese tramo.
Lo vemos con ejemplos concretos:
1) Pulso \(p(t) = u(t) - u(t-2)\)
Recuerda que, esto es equivalente a la siguiente función definida a trozos: \[ x(t) = \begin{cases} 1, & 0 \le t < 2 \\ 0, & \text{en otro caso} \end{cases} \]
En MATLAB se implementa así:
x_pulso = zeros(size(t));
tramo_pulso = (t >= 0) & (t < 2);
x_pulso(tramo_pulso) = 1;- Con
zeros(size(t))inicializamos la señal a cero, mismo tamaño quet. - La condición
(t >= 0) & (t < 2)devuelve un vector lógico, contrueen las posiciones donde la condición se cumple, yfalseen las demás. Ese vector se puede usar para indexar. - La asignación
x_pulso(tramo_pulso) = 1pone a 1 todos los elementos dex_pulsodonde la condición estrue.
2) Pulso triangular, definido a trozos: \[ x(t) = \begin{cases} t, & 0 \le t < 1 \\ 2 - t, & 1 \le t < 2 \\ 0, & \text{en otro caso} \end{cases} \]
x_tri = zeros(size(t));
tramo_subida = (t >= 0) & (t < 1);
x_tri(tramo_subida) = t(tramo_subida);
tramo_bajada = (t >= 1) & (t < 2);
x_tri(tramo_bajada) = 2 - t(tramo_bajada);- Fíjate en que se usa
x_tri(tramo_subida) = t(tramo_subida) - Usar
x_tri(tramo_subida) = tdaría lugar a un error de dimensiones, porque cada lado de la asignación tiene un tamaño distinto.
3) Trapecio, también definido a trozos: \[ x(t) = \begin{cases} t, & 0 \le t < 1 \\ 1, & 1 \le t < 3 \\ 4 - t, & 3 \le t < 4 \\ 0, & \text{en otro caso} \end{cases} \]
x_trap = zeros(size(t));
tramo_subida = (t >= 0) & (t < 1);
x_trap(tramo_subida) = t(tramo_subida);
tramo_plano = (t >= 1) & (t < 3);
x_trap(tramo_plano) = 1;
tramo_bajada = (t >= 3) & (t < 4);
x_trap(tramo_bajada) = 4 - t(tramo_bajada);Usando condiciones lógicas y asignaciones, podemos construir cualquier señal continua a trozos.
2.4 Cómo normalizar una señal
Normalizar una señal es cambiar su escala para que su amplitud tome un valor de referencia, normalmente 1. Es útil por varios motivos:
- Las señales reales (audio, medidas de un sensor…) llegan con amplitudes arbitrarias, que dependen del equipo que las registró.
- Permite comparar a simple vista señales de amplitudes muy distintas.
- Algunas funciones lo exigen:
sound(x, Fs)solo reproduce bien valores entre −1 y 1, y recorta los que se salen.
La forma más habitual es dividir por el valor máximo en valor absoluto:
x = x / max(abs(x)); % ahora max(abs(x)) vale 1La normalización x = x / max(abs(x)) transforma los valores de una señal al rango \([-1, 1]\).
2.5 Convolución
La convolución de dos secuencias discretas se define como \[y[n] = x_1[n] * x_2[n] = \sum_{k} x_1[k]\, x_2[n-k]\]
En MATLAB se calcula con y = conv(x1, x2). Hay que tener en cuenta dos cosas:
- Si \(x_1\) tiene \(N\) muestras y \(x_2\) tiene \(M\), el resultado tiene \(N + M - 1\) muestras.
- La función
convsolo trabaja con vectores, no sabe dónde está \(n = 0\), por tanto, hay que construir a mano el vector de índices del resultado (en caso de necesitarlo).
Si \(x_1[n]\) empieza en \(n_{1,\text{ini}}\) y \(x_2[n]\) empieza en \(n_{2,\text{ini}}\), el resultado empieza en \[n_{\text{ini}} = n_{1,\text{ini}} + n_{2,\text{ini}}\] y tiene \(N_1 + N_2 - 1\) muestras.
2.6 Correlación
La correlación cruzada mide el parecido entre dos señales en función del desplazamiento \(l\). En esta asignatura la definimos como \[R_{xy}[l] = \sum_{n} x[n]\, y[n+l]\]
Es muy parecida a la convolución. De hecho, si le damos la vuelta a \(\mathbf{x}\), la correlación se convierte en una convolución. Por eso, la correlación \(R_{xy}[l]\) en MATLAB se calcula con R = conv(fliplr(x), y), donde el comando fliplr(x) simplemente le da la vuelta al vector x:
Recuerda que la correlación no es conmutativa, \(R_{xy}[l]\) no es igual a \(R_{yx}[l]\), y por tanto, en MATLAB, conv(fliplr(x), y) y conv(fliplr(y), x) dan resultados reflejados.
El primer desplazamiento es aquel en el que la última muestra de \(x_1\) se solapa con la primera de \(x_2\): \[l_{\text{ini}} = n_{2,\text{ini}} - n_{1,\text{fin}}\] y tiene \(N_1 + N_2 - 1\) valores.
2.7 Autocorrelación
La autocorrelación es simplemente la correlación de una señal consigo misma: \[R_{xx}[l] = \sum_{n} x[n]\, x[n+l]\]
En MATLAB:
- se calcula igual que la correlación cruzada:
r = conv(fliplr(x), x) - los índices son muy sencillos,
l = -(N-1) : (N-1), donde \(N\) es el número de muestras de la señal.
El vector de desplazamientos de la autocorrelación es simétrico, con \(N-1\) valores negativos, uno cero y \(N-1\) positivos, siendo \(N\) el número de muestras de la señal.
Recuerda sus principales propiedades:
- Máximo en el origen. \(R_{xx}[0] = \sum_n x^2[n] = E_x\) es la energía de la señal, y se cumple \(|R_{xx}[l]| \le R_{xx}[0]\).
- Simetría par. \(R_{xx}[-l] = R_{xx}[l]\).
- Periodicidad. Si \(x[n]\) es periódica, su autocorrelación también lo es, con el mismo periodo.
2.8 Respuesta impulsional de un “efecto eco” en audio
Un eco consiste en escuchar la señal original y, un tiempo después, copias suyas más débiles. Se modela con la respuesta impulsional \[h[n] = \delta[n] + a_1\,\delta[n-n_1] + a_2\,\delta[n-n_2]\]
Recuerda que la convolución con una delta \(\delta[n-n_0]\) simplemente copia la señal y la desplaza \(n_0\) muestras, es decir:
\[ x[n] * \delta[n-n_0] = x[n-n_0]\]
Al convolucionar una señal \(x[n]\) con \(h[n]\) cada delta “copia” la señal en su posición:
\[y[n] = x[n] * h[n] = x[n] + a_1\,x[n-n_1] + a_2\,x[n-n_2]\]
Cada término tiene un significado:
- \(x[n]\): la señal original, sin retardo ni atenuación.
- \(a_1\,x[n-n_1]\): primer eco, retardado \(n_1\) muestras y multiplicado por \(a_1\).
- \(a_2\,x[n-n_2]\): segundo eco, retardado \(n_2\) muestras y multiplicado por \(a_2\).
Relación entre retardo en muestras y retardo temporal
Con frecuencia de muestreo \(F_s\), cada muestra dura \(T_s = 1/F_s\) segundos, por tanto, la relación entre retardo en muestras \(n\) y retardo temporal \(\tau\) es: \[\tau = \frac{n}{F_s} \qquad \Longleftrightarrow \qquad n = \tau \cdot F_s\]
Por ejemplo, con \(F_s = 8000\) Hz, un retardo de 0.25 s corresponde a \(n_1 = 2000\) muestras.
Significado de los coeficientes
Los coeficientes \(a_1\) y \(a_2\) indican qué fracción de la amplitud original conserva cada eco. Deben cumplir \(0 < a_i < 1\) para que el eco sea más débil que el original. Así, un 30 % de atenuación correspondería a un coeficiente \(a = 0.7\).
2.9 Ruido blanco
El ruido blanco es una señal aleatoria en la que cada muestra es independiente de todas las demás.
En MATLAB se genera con randn:
rng(0); % semilla fija (opcional): el mismo ruido cada vez, permite reproducir resultados
N = 1000;
w = randn(1, N); % N muestras gaussianas de media 0 y varianza 1randn no es rand
El comando rand genera valores uniformemente aleatorios entre 0 y 1. El comando randngenera valores aleatorios gaussianos de media 0 y varianza 1. No es lo mismo. Para simular ruido blanco, hay que usar randn.
Si queremos pasar de media cero y desviación típica 1 a una media \(\mu\) y desviación típica \(\sigma\), podemos usar:
w = mu + sigma * randn(1, N);La propiedad más importante del ruido blanco es que no está autocorrelado:
Como cada muestra es independiente de las demás, conocer \(w[n]\) no dice nada sobre \(w[n+l]\) para \(l \neq 0\). Idealmente, la autocorrelación del ruido blanco es una delta:
\[R_{ww}[l] = N\sigma^2\,\delta[l]\]
Podemos usar esta propiedad para detectar señales útiles en presencia de ruido. Al correlacionar una señal con ruido, el ruido solo aporta en \(l = 0\), mientras que la parte útil de la señal deja su huella en el resto de desplazamientos.
Puedes ejecutar el siguiente código en MATLAB para ver la autocorrelación de una secuencia de ruido blanco; deberías ver un pico central y valores muy pequeños en los desplazamientos distintos de cero:
w = randn(1, 1001);
r = conv(fliplr(w), w);
l = -1000 : 1000;
plot(l, r)2.10 Código Barker
Un código Barker es una secuencia finita de \(N\) valores \(\pm 1\) cuya autocorrelación es “casi ideal”: \[R[0] = N, \qquad |R[l]| \le 1 \quad \text{para } l \neq 0\]
Es decir, el pico central es muy alto y los lóbulos laterales son como mucho de valor 1. Solo existen para las longitudes 2, 3, 4, 5, 7, 11 y 13.
Se usan en:
- Radar (compresión de pulso). Se emite un pulso largo, que tiene más energía, modulado en fase con un código Barker. Al correlarlo en el receptor se obtiene un pico estrecho, que da buena resolución en distancia.
- Comunicaciones. Se usan para sincronización de tramas. El estándar Wi-Fi 802.11b usa el código de longitud 11 para ensanchar el espectro.
- En general, en cualquier sistema en el que haya que detectar con precisión cuándo llega una secuencia conocida, incluso con ruido.
| \(N\) | Código | Lóbulo lateral máximo | Relación pico/lóbulo |
|---|---|---|---|
| 2 | \(\{+1,\,-1\}\) ó \(\{+1,\,+1\}\) | 1 | 2 |
| 3 | \(\{+1,\,+1,\,-1\}\) | 1 | 3 |
| 4 | \(\{+1,\,+1,\,-1,\,+1\}\) ó \(\{+1,\,+1,\,+1,\,-1\}\) | 1 | 4 |
| 5 | \(\{+1,\,+1,\,+1,\,-1,\,+1\}\) | 1 | 5 |
| 7 | \(\{+1,\,+1,\,+1,\,-1,\,-1,\,+1,\,-1\}\) | 1 | 7 |
| 11 | \(\{+1,\,+1,\,+1,\,-1,\,-1,\,-1,\,+1,\,-1,\,-1,\,+1,\,-1\}\) | 1 | 11 |
| 13 | \(\{+1,\,+1,\,+1,\,+1,\,+1,\,-1,\,-1,\,+1,\,+1,\,-1,\,+1,\,-1,\,+1\}\) | 1 | 13 |
2.11 Convolución y correlación de señales continuas
Un pequeño apunte en el caso de señales continuas. Con señales discretas, conv calcula exactamente la suma de la convolución o de la correlación. Con señales continuas, en cambio, estas operaciones son integrales, y conv solo calcula la suma de los puntos del vector (Sección 2.2). Una integral se aproxima por esa suma multiplicada por la separación entre puntos: \[\int x(t)\,dt \approx \sum_k x(t_k)\,\Delta t \qquad\Longrightarrow\qquad \texttt{sum(x)*dt}\]
Por tanto, en el caso continuo la única diferencia es que hay que multiplicar por dt el resultado de conv:
- Convolución:
y = conv(x, h) * dt - Correlación cruzada:
r = conv(fliplr(x), y) * dt - Autocorrelación:
r = conv(fliplr(x), x) * dt