← Índice del material Práctica 1 — Señales, correlación y convolución

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 con plot), incluidas las definidas a trozos.
  • Calcular la convolución de dos señales con conv y 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), construir su vector de desplazamientos e interpretar la posición de su máximo como un retardo.
  • Explicar por qué, al truncar señales infinitas para guardarlas en un vector, el resultado calculado se separa del teórico en los extremos.
  • Aproximar la convolución y la correlación de señales continuas multiplicando el resultado de conv por \(T_s\), con el eje de desplazamientos en segundos.
  • Modelar un efecto eco como la convolución con una respuesta impulsional formada por deltas, pasando los retardos de segundos a muestras.
  • 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 (en el trabajo opcional).

1.2 Estructura de la práctica

El documento se divide en tres 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.
  • El trabajo opcional suele ser un ejercicio aplicado, que puedes hacer si te sobra tiempo; no se evalúa.
AdvertenciaCarpeta de trabajo y ficheros

Es altamente recomendable trabajar en una carpeta dedicada solo para esta práctica. Debes crear un archivo ejercicioX.m por cada ejercicio. En esa misma carpeta debes guardar los ficheros que se proporcionan para el trabajo de laboratorio, para que MATLAB pueda encontrarlos:

AdvertenciaTrabajo previo y cuestionario

Recuerda completar el cuestionario en Aula Virtual antes de acudir al laboratorio, cuando termines el trabajo previo. El cuestionario no tiene límite de tiempo. Puedes tenerlo abierto mientras trabajas en los ejercicios. El cuestionario es evaluable y constituye el 20% de la nota de evaluación continua de prácticas.

AdvertenciaTrabajo de laboratorio y entrega

Debes completar el trabajo de laboratorio durante la sesión y entregarlo al final. La entrega es obligatoria y se hace en Aula Virtual, subiendo a la tarea correspondiente un único archivo comprimido .zip que contenga todos los scripts. Solamente es necesaria una entrega por pareja. La nota máxima a la que puedes optar depende del número de ejercicios que hayas completado y entregado correctamente:

Fichero Ejercicio Nota máxima
ejercicio8.m Correlación en discreto 2
ejercicio9.m Convolución en discreto 3
ejercicio10.m Autocorrelación en continuo 5
ejercicio11.m Efecto eco en audio 7.5
ejercicio12.m Radar y correlación 10
ejercicio13.m Mejor resolución en radar con código Barker opcional
AdvertenciaEvaluación de los ejercicios

El/la profesor/a evaluará los ejercicios de la práctica mediante preguntas orales durante la sesión. Asegúrate de comprender tu código y los resultados que obtienes, porque tendrás que responder a preguntas sobre ellos. Las preguntas son individuales aunque trabajéis en parejas. Cuando completes un ejercicio, pide a el/la profesor/a que te lo evalúe.

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]\).

Nota

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)
Tip✏️ Ejercicio 1

Construye y dibuja con stem dos periodos completos de una senoidal discreta de cinco muestras por periodo.

Plantilla de código, completa los ???:

clear all; close all; clc;
N0 = 5;            % muestras por periodo
P  = 2;            % número de periodos completos

n = 0 : ???;       % P periodos completos son P*N0 muestras
x = cos(???);      % senoidal de N0 muestras por periodo

stem(n, x, 'filled');
grid on;
xlabel('$n$', 'Interpreter', 'latex');
ylabel('$x[n]$', 'Interpreter', 'latex');
title('Dos periodos de una senoidal de N_0 = 5 muestras por periodo');

Deberías obtener:

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íneas

Té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”
Nota

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.

Tip✏️ Ejercicio 2

Dibuja la señal \(x(t) = \cos(2\pi t)\) con N = 1000 y con N = 10. Muéstralas superpuestas en una misma figura usando plot(t, x) en el primer caso y plot(t, x, 'o-') en el segundo. ¿Qué observas?

Con N = 1000 la curva se ve suave, pero con N = 10 sale quebrada, a trozos rectos: plot se limita a unir con segmentos los pocos puntos que tiene, y ni siquiera llega a los mínimos del coseno porque ningún punto cae sobre ellos.

Plantilla de código, completa los ???:

clear all; close all; clc;
t1 = linspace(???);          % rejilla fina:  1000 puntos entre 0 y 2 s
t2 = linspace(???);          % rejilla pobre: 10 puntos entre 0 y 2 s

plot(t1, cos(2*pi*t1));
hold on;                     % sin hold on, el segundo plot borraría el primero
plot(???, ???, 'o-');
grid on;
xlabel('???', 'Interpreter', 'latex');
ylabel('???', 'Interpreter', 'latex');
legend('???', '???');
title('???');

Deberías obtener:

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] s

La herramienta clave para dibujar señales continuas a trozos son las condiciones lógicas. El método es siempre el mismo:

  1. Empezar con un vector de ceros del tamaño de t.
  2. Para cada tramo, definir la condición que lo delimita.
  3. 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 que t.
  • La condición (t >= 0) & (t < 2) devuelve un vector lógico, con true en las posiciones donde la condición se cumple, y false en las demás. Ese vector se puede usar para indexar.
  • La asignación x_pulso(tramo_pulso) = 1 pone a 1 todos los elementos de x_pulso donde la condición es true.

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) = t darí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);
NotaFunciones a trozos

Usando condiciones lógicas y asignaciones, podemos construir cualquier señal continua a trozos.

Tip✏️ Ejercicio 3

Construye en MATLAB las tres señales anteriores sobre el eje t = linspace(-1, 6, 1000) y dibújalas con plot en una misma figura, una debajo de otra, con subplot(3,1,k). Añade a cada gráfica su título y las etiquetas de los ejes.

Plantilla de código, completa los ???:

clear all; close all; clc;
N = 1000;                    % número de puntos
t = linspace(-1, 6, N);      % t en [-1, 6] s

% Aquí, las tres señales x_pulso, x_tri y x_trap:
% su código está en los tres ejemplos de arriba

subplot(3,1,???);            % panel superior
plot(t, ???, 'LineWidth', 1.5);
grid on;
title('Pulso');
xlabel('$t$ (s)', 'Interpreter', 'latex');
ylabel('$p(t)$', 'Interpreter', 'latex');

% ... y lo mismo en los otros dos paneles, con x_tri y x_trap

Deberías obtener:

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 1
NotaNormalización al intervalo \([-1, 1]\)

La 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 conv solo 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).
NotaÍndices de la convolución

El resultado empieza en la suma de los índices iniciales de x1 y de x2, y su vector de índices tiene tantos valores como la convolución. Por tanto, si n1 y n2 son los vectores de índices de x1 y de x2, se construye así:

y = conv(x1, x2);
n_ini = n1(1) + n2(1);               % índice inicial de x1 + índice inicial de x2
n = n_ini : n_ini + length(y) - 1;   % vector de índices del resultado
Tip✏️ Ejercicio 4

Calcula con MATLAB, usando el comando conv, la convolución de \[x_1[n] = \{1,\, \underset{\uparrow}{-1},\, 2\} \qquad\qquad x_2[n] = \{2,\, 3,\, \underset{\uparrow}{1}\}\]

a) Dibuja con stem las dos señales y el resultado, cada una frente a su vector de índices, en una misma figura con tres subgráficas (subplot(3,1,k)). Añade a cada gráfica su título y las etiquetas de los ejes.

Deberías obtener

\[y[n] = x_1[n] * x_2[n] = \{2,\, 1,\, 2,\, \underset{\uparrow}{5},\, 2\}, \qquad n = -3, \dots, 1\]

Plantilla de código, completa los ???:

clear all; close all; clc;
% Señales y sus índices: la flecha marca n = 0
x1 = [1 -1 2];
x2 = ???;
n1 = [-1 0 1];               % x1 empieza en n = -1
n2 = ???;                    % índices de x2

% Convolución y su vector de índices (nota «Índices de la convolución»)
y = conv(x1, x2);
n_ini = ???;                 % explicado en la nota
n = ???;                     % explicado en la nota

subplot(3,1,1); stem(???, ???, 'filled'); grid on;
title('x_1[n]');
xlabel('$n$', 'Interpreter', 'latex');
ylabel('$x_1[n]$', 'Interpreter', 'latex');

% ... y lo mismo en los otros dos paneles, con x2 y con y

La figura resultante es:

Desarrollo a mano por el método tabular:

\(n=-3\) \(n=-2\) \(n=-1\) \(n=0\) \(n=1\)
\(x_1[-1]=1\) \(2(2)\) \(3(3)\) \(1(1)\)
\(x_1[0]=-1\) \(-2(2)\) \(-3(3)\) \(-1(1)\)
\(x_1[1]=2\) \(4(2)\) \(6(3)\) \(2(1)\)
\(y[n]\) \(\mathbf{2}\) \(\mathbf{1}\) \(\mathbf{2}\) \(\mathbf{5}\) \(\mathbf{2}\)

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_{x_1x_2}[l] = \sum_{n} x_1[n]\, x_2[n+l]\]

Es muy parecida a la convolución. De hecho, si le damos la vuelta a \(\mathbf{x_1}\), la correlación se convierte en una convolución. Por eso, la correlación \(R_{x_1x_2}[l]\) en MATLAB se calcula con R = conv(fliplr(x1), x2), donde el comando fliplr(x1) simplemente le da la vuelta al vector x1.

AdvertenciaLa correlación no es conmutativa

Recuerda que la correlación no es conmutativa, \(R_{x_1x_2}[l]\) no es igual a \(R_{x_2x_1}[l]\), y por tanto, en MATLAB, conv(fliplr(x1), x2) y conv(fliplr(x2), x1) dan resultados reflejados.

NotaDesplazamientos de la correlación

El primer desplazamiento es aquel en el que la última muestra de x1 se solapa con la primera de x2, y el vector de desplazamientos tiene tantos valores como la correlación. Por tanto, si n1 y n2 son los vectores de índices de x1 y de x2, se construye así:

R = conv(fliplr(x1), x2);
l_ini = n2(1) - n1(end);             % índice inicial de x2 - índice final de x1
l = l_ini : l_ini + length(R) - 1;   % vector de desplazamientos
Tip✏️ Ejercicio 5

Calcula con MATLAB, usando los comandos conv y fliplr, la correlación \(R_{x_1x_2}[l]\) de \[x_1[n] = \{1,\, \underset{\uparrow}{-1},\, 2\} \qquad\qquad x_2[n] = \{2,\, 3,\, \underset{\uparrow}{1}\}\]

a) Dibuja con stem las dos señales y el resultado, cada una frente a su vector de índices, en una misma figura con tres subgráficas (subplot(3,1,k)). Añade a cada gráfica su título y las etiquetas de los ejes.

NOTA: puedes reutilizar el código del ejercicio anterior y es suficiente con modificar unas pocas líneas.

Deberías obtener

\[R_{x_1x_2}[l] = \{4,\, 4,\, 1,\, \underset{\uparrow}{2},\, 1\}, \qquad l = -3, \dots, 1\]

Plantilla de código, completa los ???:

clear all; close all; clc;
% Señales y sus índices: la flecha marca n = 0
x1 = [1 -1 2];
x2 = ???;
n1 = [-1 0 1];               % x1 empieza en n = -1
n2 = ???;                    % índices de x2

% Correlación y su vector de desplazamientos (nota «Desplazamientos de la correlación»)
R = conv(???, ???);          % se da la vuelta a x1 con fliplr
l_ini = ???;                 % índice inicial de x2 - índice final de x1
l = ???;                     % tantos desplazamientos como muestras tiene R

subplot(3,1,1); stem(???, ???, 'filled'); grid on;
title('x_1[n]');
xlabel('$n$', 'Interpreter', 'latex');
ylabel('$x_1[n]$', 'Interpreter', 'latex');

% ... y lo mismo en los otros dos paneles, con x2 y con R

La figura resultante es:

Desarrollo a mano por el método tabular:

\(l=-3\) \(l=-2\) \(l=-1\) \(l=0\) \(l=1\)
\(x_1[-1]=1\) \(2(2)\) \(3(3)\) \(1(1)\)
\(x_1[0]=-1\) \(-2(2)\) \(-3(3)\) \(-1(1)\)
\(x_1[1]=2\) \(4(2)\) \(6(3)\) \(2(1)\)
\(R_{x_1x_2}[l]\) \(\mathbf{4}\) \(\mathbf{4}\) \(\mathbf{1}\) \(\mathbf{2}\) \(\mathbf{1}\)

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.
NotaDesplazamientos de la autocorrelación

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]\]

NotaConvolución con deltas

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\).

Tip✏️ Ejercicio 6

Con una frecuencia de muestreo \(F_s = 1000\) Hz, construye en MATLAB la respuesta impulsional \(h[n]\) de un efecto eco en el que el primer eco llega con un retardo de 10 ms y una atenuación del 50 %, y el segundo con un retardo de 20 ms y una atenuación del 75 %.

a) Calcula los parámetros \(a_1, n_1, a_2, n_2\).

b) Construye h con zeros e introduce a mano sus tres elementos no nulos. Dibújala con stem, con el eje x en muestras.

a) Una atenuación del 50 % conserva la mitad de la amplitud, y una del 75 %, la cuarta parte: \(a_1 = 0.5\) y \(a_2 = 0.25\). Los retardos en muestras son \(n_1 = 0.010 \cdot 1000 = 10\) y \(n_2 = 0.020 \cdot 1000 = 20\), así que \[h[n] = \delta[n] + 0.5\,\delta[n-10] + 0.25\,\delta[n-20]\]

b) Como los vectores de MATLAB empiezan en 1, la muestra \(h[n]\) se guarda en h(n+1). Plantilla de código, completa los ???:

clear all; close all; clc;
Fs = 1000;               % frecuencia de muestreo (Hz)

% Primer eco: 10 ms y atenuación del 50 %
a1 = ???;
n1 = ???;

% Segundo eco: 20 ms y atenuación del 75 %
a2 = ???;
n2 = ???;

% Construcción de la respuesta impulsional
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% Recuerda que en MATLAB los índices empiezan en 1, no en 0:  %%%
%%% la muestra h[n] se guarda en h(n+1)                         %%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
h = zeros(1, n2 + 1);    % n = 0..n2
h(???) = 1;              % señal directa (n = 0)
h(???) = a1;             % primer eco    (n = n1)
h(???) = a2;             % segundo eco   (n = n2)

n = 0 : n2;
stem(n, h, 'filled'); grid on;
xlabel('$n$', 'Interpreter', 'latex');
ylabel('$h[n]$', 'Interpreter', 'latex');
title('Respuesta impulsional de un filtro efecto eco');

Deberías obtener:

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, el comando randn genera ruido blanco gaussiano de media cero y desviación típica 1:

N = 1000;
w = randn(1, N);          % N muestras gaussianas de media 0 y varianza 1
Advertenciarandn 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 media \(\mu\) y desviación típica \(\sigma\), basta con escalar y desplazar:

w = mu + sigma * randn(1, N);

La propiedad más importante del ruido blanco es que no está autocorrelado:

Nota

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)
grid on
xlabel('$l$', 'Interpreter', 'latex');
ylabel('$R_{ww}[l]$', 'Interpreter', 'latex');
title('Autocorrelación de una secuencia de ruido blanco');

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
Tip✏️ Ejercicio 7

Calcula a mano la autocorrelación de la secuencia Barker de longitud 3 usando el método tabular y verifica que su valor absoluto es menor o igual que uno para cualquier desplazamiento distinto de cero.

Secuencia: \(\{1, 1, -1\}\), con la primera muestra en \(n = 0\). Con tres muestras, los desplazamientos con solapamiento van de \(l = -2\) a \(l = 2\):

\(l=-2\) \(l=-1\) \(l=0\) \(l=1\) \(l=2\)
\(b[0]=1\) \(1(1)\) \(1(1)\) \(-1(-1)\)
\(b[1]=1\) \(1(1)\) \(1(1)\) \(-1(-1)\)
\(b[2]=-1\) \(-1(1)\) \(-1(1)\) \(1(-1)\)
\(R[l]\) \(\mathbf{-1}\) \(\mathbf{0}\) \(\mathbf{3}\) \(\mathbf{0}\) \(\mathbf{-1}\)

\[R[l] = \{-1, 0, \underset{\uparrow}{3}, 0, -1\}\]

Como toda autocorrelación, es simétrica: \(R[-l] = R[l]\).

Fuera del origen, \(|R[l]| \le 1\): el pico es tres veces mayor que cualquier lóbulo lateral.

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)\,T_s \qquad\Longrightarrow\qquad \texttt{sum(x)*Ts}\]

Por tanto, en el caso continuo la única diferencia es que hay que multiplicar por Ts el resultado de conv:

  • Convolución: y = conv(x, h) * Ts
  • Correlación cruzada: r = conv(fliplr(x1), x2) * Ts
  • Autocorrelación: r = conv(fliplr(x), x) * Ts

3 Trabajo de laboratorio

Tip✏️ Ejercicio 8: Correlación en discreto

Considera las señales

\[f[n] = \left(\tfrac{1}{3}\right)^n u[n-1], \qquad g[n] = 2\, u[n-2]\]

cuya correlación cruzada teórica es

\[ R_{fg}[l] = \begin{cases} 3^{\,l-1}, & l \le 1 \\ 1, & l > 1 \end{cases} \]

a) Las dos señales son infinitas, así que en MATLAB hay que cortar en algún momento. Construye el vector de índices como n = 0:9, o sea \(N = 10\) muestras. Después, construye las dos señales f = ???, g = ???, y dibuja cada una con stem en los dos primeros paneles de un subplot(3,1,k).

b) Calcula su correlación cruzada con MATLAB y compárala con la correlación teórica en una misma gráfica, usando stem para la calculada y plot para la teórica (ver solución)

c) Explica qué observas. ¿Coincide la solución calculada con la teórica? ¿Por qué?

Tip✏️ Ejercicio 9: Convolución en discreto

Considera las mismas señales del ejercicio anterior,

\[f[n] = \left(\tfrac{1}{3}\right)^n u[n-1], \qquad g[n] = 2\, u[n-2]\]

cuya convolución teórica es

\[ y[n] = \begin{cases} 0, & n \le 2 \\ 1 - \left(\tfrac{1}{3}\right)^{n-2}, & n > 2 \end{cases} \]

a) Las dos señales son infinitas, así que en MATLAB hay que cortar en algún momento. Construye el vector de índices como n = 0:9, o sea \(N = 10\) muestras. Después, construye las dos señales f = ???, g = ??? (son las del ejercicio anterior), y dibuja cada una con stem en los dos primeros paneles de un subplot(3,1,k).

b) Calcula su convolución con MATLAB y compárala con la convolución teórica en una misma gráfica, usando stem para la calculada y plot para la teórica (ver solución)

c) Explica qué observas. ¿Coincide la solución calculada con la teórica? ¿Por qué?

Nota: Puedes reutilizar todo el código del ejercicio anterior, cambiando únicamente unas pocas líneas.

Tip✏️ Ejercicio 10: Autocorrelación en continuo

En este ejercicio, a diferencia de los anteriores, trabajarás con señales continuas. La única diferencia es que después de usar conv para calcular la convolución o la correlación, hay que multiplicar por el tiempo de muestreo \(T_s\) para que el cálculo de la integral sea el correcto, según lo descrito en la Sección 2.11. Igualmente, el vector de desplazamientos también hay que multiplicarlo por \(T_s\) para que esté en segundos. Usa la plantilla de código proporcionada en ejercicio10_plantilla.m.

a) Genera una figura con 4 × 2 subplots. Dibuja con plot en la columna de la izquierda las siguientes señales. Usa para las cuatro el mismo vector de tiempos, con rango \(t \in [0, 5]\) s y \(N = 1001\) puntos, es decir, t = linspace(0, 5, 1001). Esto implica que el tiempo de muestreo es \(T_s = 5/1000 = 5\) ms.

  1. \(x_1(t) = u(t) - u(t - 2)\), un pulso que va de 0 a 2 s
  2. \(x_2(t) = \cos(4\pi t)\), de periodo 0.5 s
  3. \(x_3(t)\) = ruido blanco gaussiano de media nula y varianza unidad (randn(1, N), ver Sección 2.9)
  4. \(x_4(t)\) es un pulso codificado con código Barker que puedes construir con x4 = repelem(b, 77), siendo b el vector que contiene el código Barker de longitud 13.

NOTA: la función repelem repite cada elemento del vector b 77 veces, de manera que la señal resultante tiene 13 x 77 = 1001 puntos, igual que las demás.

b) Calcula la autocorrelación de cada señal y dibújala en la columna de la derecha (revisa la sección Sección 2.11 para ver cómo se calcula). Igualmente, revisa la sección Sección 2.7 para ver cómo se construye el vector de desplazamientos, y recuerda que hay que multiplicarlo por \(T_s\) para que esté en segundos.

c) Trata de explicar los siguientes aspectos:

  1. ¿Por qué todas tienen su máximo en el origen?
  2. ¿Por qué la autocorrelación de la senoidal oscila? ¿Por qué se estrecha en los extremos?
  3. ¿Por qué el ruido blanco tiene ese pico abrupto en el origen y es prácticamente cero en el resto? ¿Cómo podemos aprovechar esto para detectar periodicidades en señales muy ruidosas?
  4. ¿Qué diferencia la autocorrelación del código Barker respecto a la del pulso rectangular? ¿Por qué puede ser útil en comunicaciones digitales?

a)

Tip✏️ Ejercicio 11: Efecto eco en audio

Carga el fichero datos_voz.mat, que contiene una señal de audio (variable x) y su frecuencia de muestreo (variable Fs). Puedes escuchar el audio con el comando sound(x, Fs).

a) Normaliza la señal para que su valor máximo en valor absoluto sea 1, con x = x / max(abs(x)) (ver Sección 2.4). Después dibújala en el panel superior de una figura con 3 × 1 subplots, con el eje temporal en segundos, es decir, t = (0:length(x)-1)*Ts.

b) Elige los parámetros \(a_1, n_1, a_2, n_2\) de la respuesta impulsional del efecto eco,

\[h[n] = \delta[n] + a_1\,\delta[n-n_1] + a_2\,\delta[n-n_2]\] para que el primer eco esté retardado 0.25 s con una atenuación del 30 % y el segundo eco esté retardado 0.5 s con una atenuación del 60 %. Puedes reutilizar el código del Ejercicio 6 (Sección 2.8).

c) Construye la señal \(h[n]\). Inicialízala con zeros(n2 + 1, 1) e introduce a mano los elementos no nulos (la muestra de la señal directa y las dos de los ecos) en los índices adecuados. Dibújala en el segundo panel de la figura, con el eje x en muestras.

d) Calcula la convolución de la señal con la respuesta impulsional y dibújala en el tercer panel de la figura, con el eje x en segundos. Por último, escucha el resultado y comprueba que el efecto es el esperado.

b) Una atenuación del 30 % conserva el 70 % de la amplitud, y una del 60 %, el 40 %: \(a_1 = 0.7\) y \(a_2 = 0.4\). Los retardos en muestras son \(n_1 = 0.25\,F_s\) y \(n_2 = 0.5\,F_s\) (ver Sección 2.8).

Tip✏️ Ejercicio 12: Radar y correlación

Un radar emite un pulso formado por cuatro periodos completos de una senoidal discreta de 10 muestras por ciclo: \[p[n] = \cos\left(\frac{2\pi}{10}\, n\right), \qquad n = 0, 1, \dots, 39\]

Dicho pulso se refleja en los objetos a su alcance y la señal rebotada se registra en un receptor. El eco de cada objeto llega con un retardo de \(D\) muestras que depende de su distancia. Debido a la frecuencia de la onda empleada y al periodo de muestreo del receptor, se sabe que cada muestra de retardo equivale a unos 150 m de distancia entre el radar y el objeto.

a) Dibuja el pulso completo con stem en el panel superior de una figura con 3 × 1 subplots, con el eje x en muestras.

b) Usa la función proporcionada x_n = senyal_radar(dist_obj1, dist_obj2, pulso) para generar una señal recibida ficticia y dibújala en el segundo panel de la misma figura, con el eje x en muestras. Las variables dist_obj1 y dist_obj2 deben ir en metros y pulso es el vector del apartado anterior. Si alguno de los tres no es el esperado, la función se detiene con un mensaje que explica qué hay que corregir. Elige sus valores para simular dos objetos a distancias de 15 km y 45 km.

c) Calcula la correlación cruzada \(R_{px}[l]\) entre el pulso y la señal recibida y dibújala en el panel inferior de la misma figura.

d) Calcula los máximos de la correlación y sus ubicaciones. A partir de esos datos, determina la distancia de los objetos al radar. Si quieres hacerlo de forma automática puedes usar [picos, pos] = findpeaks(r_px, 'NPeaks', 2, 'SortStr', 'descend','MinPeakDistance', length(pulso)), que devuelve el valor de los picos y sus índices.

e) Reduce la distancia entre los dos objetos, por ejemplo, 15 km y 16 km. ¿Qué ocurre con la correlación? ¿Se pueden distinguir los dos objetos? ¿Qué necesitaríamos en la correlación para poder distinguirlos?

Como ejemplo, colocamos los objetos a 15 km y a 45 km.

Objeto 1: retardo = 100 muestras, distancia = 15.00 km
Objeto 2: retardo = 300 muestras, distancia = 45.00 km

4 Trabajo opcional

Tip✏️ Ejercicio 13: Mejor resolución en radar con código Barker

En el ejercicio del radar, el pulso senoidal no permitía distinguir dos objetos a 15 km y 16 km. Vamos a sustituirlo por un pulso de casi la misma duración construido con un código Barker. Puedes reciclar casi todo el código del ejercicio del radar. Solamente cambia la construcción del pulso y dos detalles que dependían de él:

  • el pulso senoidal tenía 40 muestras; para una duración similar, usa un código Barker de longitud 13 con 3 muestras por símbolo (39 muestras), con repelem como en el ejercicio de autocorrelación.

  • el eje del pulso, que ahora será n_pulso = 0 : length(pulso)-1

  • la separación mínima entre picos de findpeaks, que pasa a 'MinPeakDistance', 3 porque el pico del código Barker es mucho más estrecho.

Objeto 1: retardo = 100 muestras, distancia = 15.00 km
Objeto 2: retardo = 107 muestras, distancia = 16.05 km