Práctica 0: Introducción a Matlab

Señales y Sistemas Lineales

1 Objetivos y cómo usar este documento

Este Prelab es una guía para tu primer contacto con MATLAB. Está pensado para que lo sigas escribiendo tú mismo el código en un script: no basta con leerlo, tienes que reproducir cada ejemplo y experimentar cambiando valores.

Al terminar deberías ser capaz de:

  • Crear variables, vectores y evaluar funciones matemáticas.
  • Utilizar todos los comandos básicos de representación gráfica.
  • Entender una idea central de la asignatura: en un ordenador no existen las funciones continuas; lo que vemos como una curva continua es en realidad un conjunto de puntos muy próximos unidos por segmentos rectos.

2 Instalación de Matlab

Tienes al menos estas dos opciones:

Software UV (si tienes tiempo)

La aplicación de MATLAB se puede instalar en tu ordenador personal desde la web de Software UV.

  1. Regístrate con tu correo UV, añade al carrito la versión de MATLAB que necesites y sigue las instrucciones.

MATLAB Online (muy rápido)

Alternativamente, puedes usar la versión online de MATLAB desde cualquier navegador. Para ello, simplemente:

  1. Accede a MATLAB Online.
  2. Accede a la aplicación e introduce tu correo UV.
  3. Se te redirigirá al servicio de autenticación. Inicia sesión con tus credenciales UV.
  4. Deberías poder acceder a Matlab Online inmediatamente.

3 Flujo de trabajo recomendado

Matlab cuenta con cuatro ventanas principales:

  • Command Window: donde escribes órdenes y ves resultados de forma inmediata.
  • Editor: donde escribes scripts con múltiples líneas de código y funciones que puedes ejecutar de golpe.
  • Workspace: aquí ves las variables que has creado y que están en memoria.
  • Current Folder: donde ves los archivos en el directorio de trabajo actual.
Figura 1: Ventanas principales de MATLAB. Si tu disposición es distinta, puedes recuperar esta desde Layout → Default.
ImportanteTrabajaremos siempre con scripts (archivos .m), nunca con Live Scripts

Abre MATLAB y crea un archivo nuevo desde New → Script (o Ctrl/Cmd + N). Guárdalo, por ejemplo, como prelab.m. Escribe en él el código de cada apartado y ejecútalo con el botón Run (o la tecla F5).

Matlab dispone de Live Scripts (.mlx), que permiten mezclar texto, ecuaciones y código en un mismo documento, de forma parecida a los Jupyter Notebooks de Python. Sin embargo, se desaconseja su uso en esta asignatura.

Antes de empezar, tres comandos que usarás constantemente para “limpiar” el entorno:

clc         % limpia la ventana de comandos (Command Window)
clear all   % borra todas las variables de la memoria (Workspace)
close all   % cierra todas las figuras abiertas
ImportanteMantén tu entorno limpio

Siempre que empieces un nuevo script, ejecuta clear all; close all; clc; en la primera línea del mismo para limpiar el entorno. Esto evita errores y confusiones debidos a variables antiguas, figuras abiertas o restos de ejecuciones anteriores.

AdvertenciaMejor un script por ejercicio

Para empezar con MATLAB, es mejor crear un script por cada ejercicio. Así, si algo falla, puedes depurarlo sin afectar a otros ejercicios. Además, te permite guardar tu trabajo y volver a él más tarde.

Y dos formas de pedir ayuda sobre cualquier comando (escríbelas en la Command Window), por ejemplo, para saber cómo funciona el comando plot:

  • help plot: muestra ayuda breve en texto sobre el comando plot
  • doc plot: abre la documentación completa en el navegador de ayuda
ImportanteSi tu código no funciona

Siempre que haya un error de ejecución (cosa bastante frecuente), MATLAB te lo indicará en la Command Window. Lee el mensaje de error con atención: suele indicar la línea donde se ha producido y el tipo de error. Si no sabes cómo solucionarlo, busca en la documentación, y, si no lo encuentras, pregunta a tu profesor o a tus compañeros.

4 Variables y operaciones básicas

En MATLAB una variable se crea simplemente asignándole un valor con =. El punto y coma ; al final de una línea evita que se muestre el resultado; si lo omites, MATLAB imprime el valor. Todo lo que va detrás de % es un comentario.

a = 3;          % con ; no se muestra nada
b = 4           % sin ; MATLAB muestra: b = 4
c = a + b       % suma
d = a * b       % producto
e = b / a       % división
f = b ^ 2       % potencia
b = 4
c = 7
d = 12
e = 1.3333
f = 16

MATLAB conoce las constantes habituales y las funciones matemáticas usuales:

pi              % número pi
r = 2;
area = pi * r^2 % área de un círculo de radio 2
raiz = sqrt(2)  % raíz cuadrada
ans = 3.1416
area = 12.5664
raiz = 1.4142

5 Vectores

Las señales las representaremos como vectores de valores. En este apartado veremos cómo generarlos y cómo acceder a sus elementos (indexación).

5.1 Generación de vectores

Hay dos formas fundamentales de construir un vector de valores equiespaciados.

1. El operador dos puntos inicio:paso:fin genera valores desde inicio hasta fin separados por paso:

t = 0:0.5:3     % de 0 a 3 en pasos de 0.5
t = 1x7 double
         0    0.5000    1.0000    1.5000    2.0000    2.5000    3.0000

2. La función linspace(inicio, fin, N) genera exactamente N valores equiespaciados entre inicio y fin. Es la más cómoda cuando sabes cuántos puntos quieres:

x = linspace(0, 1, 5)   % 5 valores igualmente espaciados entre 0 y 1
x = 1x5 double
         0    0.2500    0.5000    0.7500    1.0000

Cuando los valores no siguen ningún patrón, puedes escribirlos directamente entre corchetes, separados por comas o espacios:

v = [3, 1, 4, 1, 5]     % vector con valores arbitrarios
v = 1x5 double
     3     1     4     1     5

5.2 Indexación de vectores

Podemos consultar cuántos elementos tiene un vector y acceder a ellos por su índice:

AdvertenciaEn MATLAB el primer índice es 1

¡El primer elemento de un vector es el número 1, no el 0! Esto es distinto de lenguajes como C, Java o Python, donde se empieza a contar por el 0. Así, x(1) es el primer elemento y x(end) el último. Se puede operar con end, por ejemplo, x(end-1) sería el penúltimo elemento.

x = linspace(0, 1, 5);
length(x)       % número de elementos
x(1)            % primer elemento
x(end)          % último elemento (palabra clave end)
x(2:4)          % elementos del 2 al 4
ans = 5
ans = 0
ans = 1
ans = 1x3 double
    0.2500    0.5000    0.7500
Tip✏️ Ejercicio 1

Crea un vector de tiempos t que vaya de 0 a 2 s en pasos de 0.25 s de dos formas: primero con el operador : y después con linspace. Para usar linspace necesitas saber cuántos puntos tiene el vector: calcúlalo a mano antes de escribir el código y comprueba después con length que ambos vectores tienen el mismo número de elementos.

Si un vector va de inicio a fin con un paso constante, su número de puntos es \[ N = \frac{\text{fin} - \text{inicio}}{\text{paso}} + 1 = \frac{2 - 0}{0.25} + 1 = 9 \] El \(+1\) aparece porque se cuentan los dos extremos: 8 intervalos de 0.25 s necesitan 9 puntos.

clear all; close all; clc;
t1 = 0:0.25:2               % con el operador dos puntos
t2 = linspace(0, 2, 9)      % con linspace: 9 puntos
length(t1)
length(t2)
t1 = 1x9 double
         0    0.2500    0.5000    0.7500    1.0000    1.2500    1.5000    1.7500    2.0000
t2 = 1x9 double
         0    0.2500    0.5000    0.7500    1.0000    1.2500    1.5000    1.7500    2.0000
ans = 9
ans = 9
Tip✏️ Ejercicio 2

Crea el vector de tiempos t = 0:0.1:5 y, usando solo índices, extrae:

  1. Las 5 primeras muestras.
  2. Las 5 últimas muestras. Pista: dentro de los paréntesis se puede operar con end; por ejemplo, end-1 es el penúltimo índice.
  3. Las muestras correspondientes a los instantes comprendidos entre 1 y 2 s (ambos incluidos).

Por último, calcula a mano en qué índice se encuentra el instante t = 3 s y comprueba tu respuesta en MATLAB.

Como el vector empieza en t = 0 con paso 0.1 s, el instante \(t_k\) está en el índice \[ k = \frac{t_k - 0}{0.1} + 1 \] Así, t = 1 está en el índice 11, t = 2 en el 21 y t = 3 en el 31 (y no en el 30: recuerda que el primer índice es 1, que corresponde a t = 0).

clear all; close all; clc;
t = 0:0.1:5;
primeras = t(1:5)           % a) índices 1 a 5
ultimas  = t(end-4:end)     % b) los 5 últimos índices
tramo    = t(11:21)         % c) de t = 1 s (índice 11) a t = 2 s (índice 21)
t(31)                       % instante en el índice 31: vale 3
primeras = 1x5 double
         0    0.1000    0.2000    0.3000    0.4000
ultimas = 1x5 double
    4.6000    4.7000    4.8000    4.9000    5.0000
tramo = 1x11 double
    1.0000    1.1000    1.2000    1.3000    1.4000    1.5000    1.6000    1.7000    1.8000    1.9000    2.0000
ans = 3
Tip✏️ Ejercicio 3

Al indexar también se puede usar un paso, igual que al crear vectores: x(inicio:paso:fin). Esto permite realizar una operación muy habitual en procesado de señal: el diezmado (downsampling). Diezmar una señal discreta \(x[n]\) por un factor entero \(M\) consiste en quedarse con una de cada \(M\) muestras y descartar el resto.

Crea el vector t = linspace(0, 1, 101) y diézmalo por un factor \(M = 10\) usando indexación. Responde, y comprueba en MATLAB:

  1. Imprime el vector diezmado y comprueba que contiene solamente uno de cada 10 elementos del vector original.
  2. ¿Cuál es la separación entre muestras de t? ¿Y del vector diezmado?
  3. ¿Cuántas muestras tiene el vector diezmado? ¿Y el original?
  1. Con t(1:M:end) nos quedamos con los índices 1, 11, 21, …, 101. Para comprobarlo, basta con ver que cada elemento del vector diezmado coincide con el elemento correspondiente del original: el segundo elemento de td es t(11), el tercero es t(21), etc.

  2. El vector t tiene 101 puntos entre 0 y 1, es decir, 100 intervalos de \(1/100 = 0.01\) s. Al quedarnos con una de cada 10 muestras, la separación pasa a ser \(10 \cdot 0.01 = 0.1\) s.

  3. El original tiene 101 muestras y el diezmado \(\frac{1 - 0}{0.1} + 1 = 11\).

clear all; close all; clc;
t  = linspace(0, 1, 101);
M  = 10;

% a) diezmado y comprobación
td = t(1:M:end)                   % una de cada M muestras
td(2)                             % segundo elemento del diezmado...
t(11)                             % ...coincide con el elemento 11 del original
td(3)                             % tercer elemento del diezmado...
t(21)                             % ...coincide con el elemento 21 del original

% b) separación entre muestras
paso_original = t(2) - t(1)       % 0.01 s
paso_diezmado = td(2) - td(1)     % 0.1 s

% c) número de muestras
N_original = length(t)            % 101 muestras
N_diezmado = length(td)           % 11 muestras
td = 1x11 double
         0    0.1000    0.2000    0.3000    0.4000    0.5000    0.6000    0.7000    0.8000    0.9000    1.0000
ans = 0.1000
ans = 0.1000
ans = 0.2000
ans = 0.2000
paso_original = 0.0100
paso_diezmado = 0.1000
N_original = 101
N_diezmado = 11

6 Operaciones elemento a elemento

Para dibujar funciones debemos evaluar una expresión matemática sobre cada elemento de un vector. Por ejemplo, si t es un vector de tiempos, queremos calcular \(y = t^2\) para cada instante de tiempo.

En MATLAB un vector es simplemente una matriz de una sola fila (o columna), y los operadores *, / y ^ siguen las reglas del álgebra matricial. Por eso, si escribimos y = t^2, MATLAB intenta calcular el producto matricial t*t, que no está definido para un vector fila, y da un error. Para que la operación se aplique elemento a elemento, MATLAB usa los operadores con un punto delante: .*, ./ y .^.

t = 0:1:4;
y = t.^2        % eleva al cuadrado CADA elemento: [0 1 4 9 16]
y = 1x5 double
     0     1     4     9    16
AdvertenciaCon punto y sin punto no es lo mismo

La instrucción t.^2 eleva al cuadrado cada elemento (lo que queremos para una señal). En cambio, la instrucción t^2 intenta hacer la potencia matricial del vector y da error. La misma distinción existe entre .* (elemento a elemento) y * (producto matricial), y entre ./ y /. Para trabajar con señales usamos casi siempre las versiones con punto. Si uno de los dos operandos es un escalar (por ejemplo 2*t o t/2), ambas versiones dan el mismo resultado.

t = 0:1:4;
a = t .* t      % producto elemento a elemento: igual que t.^2
b = 2 .* t      % multiplicar por un escalar no necesita punto, pero se admite
c = t ./ (t + 1) % división elemento a elemento
a = 1x5 double
     0     1     4     9    16
b = 1x5 double
     0     2     4     6     8
c = 1x5 double
         0    0.5000    0.6667    0.7500    0.8000
AdvertenciaPodría hacerse con un bucle for, pero…

Es perfectamente factible recorrer todos los elementos de un vector y, por ejemplo, elevarlos al cuadrado y guardarlos en otro vector. Sin embargo, es mucho más eficiente y elegante usar los operadores con punto. MATLAB está optimizado para trabajar con vectores y matrices, y evitar bucles innecesarios.

7 Funciones matemáticas sobre vectores

La mayoría de funciones de MATLAB (sin, cos, exp, abs, sqrt, …) se aplican automáticamente a cada elemento del vector. Esto nos permite evaluar una señal de un solo golpe:

t = linspace(0, 2*pi, 8);
y = sin(t)      % el seno de cada uno de los 8 instantes
y = 1x8 double
         0    0.7818    0.9749    0.4339   -0.4339   -0.9749   -0.7818   -0.0000

Recuerda que los ángulos van en radianes. Combinando lo anterior ya podemos definir señales como \(x(t) = e^{-t}\cos(2\pi t)\):

t = linspace(0, 3, 6);
x = exp(-t) .* cos(2*pi*t)   % ¡ojo al .* entre las dos partes!
x = 1x6 double
    1.0000   -0.4440    0.0931    0.0511   -0.0734    0.0498

7.1 Sumatorios

En muchas fórmulas de la asignatura aparece un sumatorio: la suma de todos los valores de una secuencia. En MATLAB, sumar todos los elementos de un vector es inmediato con la función sum. Es decir, sum(v) calcula el sumatorio \(\sum_{n=1}^{N} v[n]\), donde \(N\) es el número de elementos del vector.

NotaÍndices en MATLAB y en las fórmulas

En las fórmulas de este documento escribimos los sumatorios con \(n = 1, \dots, N\) porque así se numeran los elementos de un vector en MATLAB. En teoría es habitual numerar las muestras desde \(n = 0\) (como haremos al dibujar con stem); el resultado de la suma es el mismo, solo cambia la etiqueta de cada muestra.

v = [3, 1, 4, 1, 5];
s = sum(v)          % 3 + 1 + 4 + 1 + 5 = 14
s = 14

Combinando sum con las operaciones elemento a elemento podemos evaluar muchas de las fórmulas para señales discretas. Un ejemplo importante es la potencia media de una señal discreta de \(N\) muestras: \[ P = \frac{1}{N}\sum_{n=1}^{N} |x[n]|^2 \]

Vamos a comprobar un resultado clásico: la potencia media de una senoide de amplitud \(A\) vale \(A^2/2\), sea cual sea su frecuencia 1

clear all; clc;
A = 2;                       % amplitud
f = 5;                       % frecuencia (Hz)
t = linspace(0, 1, 1000);    % 1 segundo de señal

x = A * sin(2*pi*f*t);       % senoide

P = sum(x.^2) / length(x)    % potencia media estimada
Pteorica = A^2 / 2           % valor teórico
P = 1.9980
Pteorica = 2

Fíjate en el orden de las operaciones: primero x.^2 eleva al cuadrado cada muestra, después sum las suma todas y, por último, dividimos entre el número de muestras. El valor estimado coincide con el teórico \(A^2/2\) salvo un pequeño error debido a que trabajamos con un número finito de muestras.

Nota

La teoría dice que la potencia media de una senoide es \(A^2/2\) independientemente de la frecuencia. En el ejemplo anterior, puedes cambiar f (por ejemplo a 1, 10 o 50 Hz) y verás que el resultado apenas cambia. Eso sí, hay un límite: si pruebas con f = 999 obtendrás una potencia prácticamente nula, porque todas las muestras caen justo en los ceros de la senoide. Cuando hay muy pocas muestras por periodo, la señal muestreada deja de describir bien la senoide (lo estudiaremos al hablar de muestreo).

7.2 Convoluciones

La convolución es una de las operaciones centrales de la asignatura: la salida de un sistema lineal e invariante en el tiempo (LTI) es la convolución de la señal de entrada con la respuesta al impulso del sistema. Eso quiere decir que cualquier filtro digital lineal e invariante se aplica mediante una convolución. Esta operación se denota mediante el símbolo \(*\) (¡no confundir con el operador * de MATLAB, que es el producto matricial!). Para dos señales discretas, se define la convolución como: \[ y[n] = x[n] * h[n]=(x * h)[n] = \sum_{k} x[k]\,h[n-k] \]

En MATLAB no necesitamos programar esa suma a mano: el comando conv(x, h) la calcula directamente. El resultado tiene length(x) + length(h) - 1 elementos.

x = [1 2 3];
h = [1 1];
y = conv(x, h)      % resultado: [1 3 5 3]
y = 1x4 double
     1     3     5     3

Podemos comprobar el resultado a mano deslizando h sobre x y sumando los productos que se solapan en cada posición: \(1\), \(1+2\), \(2+3\) y \(3\), es decir, [1 3 5 3]. Como x tiene 3 elementos y h tiene 2, la salida tiene \(3 + 2 - 1 = 4\) elementos.

8 Representación gráfica: los comandos básicos

Llega uno de los aspectos más útiles: dibujar señales. El comando principal es plot(x, y), que dibuja los puntos (x, y) y los une con líneas rectas. Antes de empezar a dibujar, una advertencia importante:

AdvertenciaEmpieza cada figura con el entorno limpio

Verás que todos los ejemplos que dibujan una figura comienzan por clear all; close all; clc;. No es un capricho: si no lo haces, es muy fácil que el nuevo dibujo se contamine con restos del anterior. Por ejemplo:

  • Si en la figura anterior quedó activo hold on, la nueva señal se superpone a la anterior en lugar de sustituirla.
  • Si la figura anterior estaba dividida con subplot, MATLAB sigue dibujando en esa división y tu gráfica aparece en una celda pequeña.
  • Una variable con el mismo nombre (por ejemplo t) puede conservar un valor antiguo de otro apartado y falsear el resultado.

Por este motivo, usamos clear all para limpiar las variables, close all para cerrar las figuras abiertas y clc para limpiar la ventana de comandos.

clear all; close all; clc;
t = linspace(0, 2, 200);
x = sin(2*pi*t);
plot(t, x)

ImportanteEn un ordenador no existen las funciones continuas

La curva anterior parece continua, pero plot solo ha dibujado 200 puntos unidos por segmentos rectos. Compruébalo dibujando la misma senoide con muy pocos puntos:

clear all; close all; clc;
t1 = linspace(0, 2, 9);      % solo 9 puntos
t2 = linspace(0, 2, 200);    % 200 puntos
plot(t1, sin(2*pi*t1), 'o-', 'LineWidth', 1.5)
hold on
plot(t2, sin(2*pi*t2), 'LineWidth', 1.5)
hold off
legend('9 puntos', '200 puntos')

Con 9 puntos se ven claramente los segmentos rectos; al aumentar el número de puntos, los segmentos son tan cortos que el ojo percibe una curva continua.

Una figura sin etiquetas no suele ser muy útil. Estos son los comandos que deberías usar cada vez que presentes una gráfica:

clear all; close all; clc;
t = linspace(0, 2, 200);
x = sin(2*pi*t);
plot(t, x, 'LineWidth', 2)   % grosor de la línea
xlabel('Tiempo t (s)')       % etiqueta del eje horizontal
ylabel('x(t)')               % etiqueta del eje vertical
title('Señal senoidal de 1 Hz')  % título
grid on                      % rejilla de fondo

Tip✏️ Ejercicio 4

Representa la señal \(x(t) = e^{-t}\) para \(t \in [0, 5]\) usando 300 puntos, con una línea azul continua de grosor 2. Añade etiquetas a los ejes, un título y la rejilla.

clear all; close all; clc;
t = linspace(0, 5, 300);
x = exp(-t);
plot(t, x, 'b-', 'LineWidth', 2)
xlabel('t (s)'); ylabel('x(t) = e^{-t}')
title('Exponencial decreciente')
grid on

8.1 Varias señales en una misma figura: hold on

Por defecto, cada plot borra el dibujo anterior. Para superponer varias señales usamos hold on después del primer plot. Además, con legend podemos añadir una leyenda que las identifica.

NotaEstilos de línea y color

El tercer argumento de plot combina color y estilo en un texto corto.

  • Colores: 'b' azul, 'r' rojo, 'g' verde, 'k' negro, 'm' magenta.
  • Estilos: '-' continua, '--' discontinua, ':' punteada, '-.' raya-punto.
  • Marcadores: 'o', '*', '.', 's' (cuadrado).

Por ejemplo 'ro' son círculos rojos sin línea, y 'g--' una línea verde discontinua.

clear all; close all; clc;
t = linspace(0, 2, 200);
plot(t, sin(2*pi*t), 'b-',  'LineWidth', 2)   % azul, línea continua
hold on
plot(t, cos(2*pi*t), 'r--', 'LineWidth', 2)   % rojo, línea discontinua
hold off
xlabel('Tiempo t (s)')
ylabel('Amplitud')
title('Seno y coseno')
legend('sin(2\pi t)', 'cos(2\pi t)')
grid on

Tip✏️ Ejercicio 5

En una misma figura y forma superpuesta, dibuja \(x_1(t) = \sin(2\pi t)\) y su versión amortiguada \(x_2(t) = e^{-t}\sin(2\pi t)\) para \(t \in [0, 3]\). Usa una línea azul continua para la primera y roja discontinua para la segunda, ambas de grosor 2. Incluye una leyenda con las fórmulas de cada gráfica para identificarlas.

8.2 Ajustar los ejes: axis, xlim, ylim

A veces queremos controlar el rango que se ve. xlim y ylim fijan los límites de cada eje; axis permite hacerlo con un solo comando:

clear all; close all; clc;
t = linspace(0, 2, 200);
plot(t, sin(2*pi*t), 'LineWidth', 2)
xlabel('t (s)'); ylabel('x(t)'); grid on
xlim([0 1])          % mostrar solo de t=0 a t=1
ylim([-1.5 1.5])     % rango vertical
% Equivalente en una línea:  axis([0 1 -1.5 1.5])

8.3 Varias gráficas en la misma ventana: subplot

El comando subplot(filas, columnas, k) divide la ventana de la figura en una cuadrícula de filas × columnas celdas y activa la celda número k, de modo que el siguiente plot se dibuja dentro de ella.

¿Cómo se numera k? Las celdas se cuentan empezando por 1, recorriendo la cuadrícula de izquierda a derecha y de arriba abajo (como se lee un texto). Por ejemplo, en una cuadrícula de 2×3 (2 filas, 3 columnas) los índices quedan así:

+-----+-----+-----+
|  1  |  2  |  3  |   <- primera fila
+-----+-----+-----+
|  4  |  5  |  6  |   <- segunda fila
+-----+-----+-----+

En el ejemplo a continuación, usamos una cuadrícula de 2 filas × 1 columna, así que k = 1 es la celda de arriba y k = 2 la de abajo:

clear all; close all; clc;
t = linspace(0, 2, 200);

subplot(2, 1, 1)                 % cuadrícula de 2 filas x 1 columna, celda 1 (arriba)
plot(t, sin(2*pi*t), 'LineWidth', 2)
title('Seno'); ylabel('x_1(t)'); grid on

subplot(2, 1, 2)                 % celda 2 (abajo)
plot(t, cos(2*pi*t), 'r', 'LineWidth', 2)
title('Coseno'); xlabel('t (s)'); ylabel('x_2(t)'); grid on

Tip✏️ Ejercicio 6

Usando subplot, dibuja en una figura de 2×1 la señal \(x(t) = \cos(2\pi t)\) (arriba, línea azul continua) y su valor absoluto \(|x(t)|\) (abajo, línea roja continua), para \(t \in [0, 2]\) con 400 puntos. Usa grosor 2 en ambas.

NotaAplicación: el rectificador de onda completa

Tomar el valor absoluto de una señal —convertir los tramos negativos en positivos, “doblándolos” hacia arriba— es exactamente lo que hace un rectificador de onda completa, un circuito electrónico básico presente en casi cualquier fuente de alimentación para pasar de corriente alterna a continua. Puedes ver cómo funciona en este vídeo.

8.4 Señales discretas: stem

El comando plot une los puntos con líneas rectas, lo que sugiere una señal continua. Cuando trabajamos con una señal discreta \(x[n]\) (una secuencia de muestras), la forma correcta de representarla es con stem, que dibuja cada muestra como una línea vertical rematada por un círculo (un “pirulí”). Con la opción 'filled' los círculos salen rellenos.

clear all; close all; clc;
n = 0:8;                     % índices de muestra
x = sin(2*pi*n/8);           % una secuencia discreta (un periodo)

stem(n, x, 'filled', 'LineWidth', 1.2)
title('Señal discreta representada con stem')
xlabel('n'); ylabel('x[n]'); grid on

NotaStem vs plot

El comando stem es, conceptualmente, el más adecuado para representar señales discretas, puesto que las mismas solo existen en determinados instantes de tiempo. Alternativamente, es posible usar el comando plot con marcadores pero sin líneas.

Este es un ejemplo de la misma señal discreta representada con el comando plot:

clear all; close all; clc;
n = 0:8;                     % índices de muestra
x = sin(2*pi*n/8);           % una secuencia discreta (un periodo)

plot(n, x, 'o', 'LineWidth', 1.2)
title('Señal discreta representada con plot (sin trazo)')
xlabel('n'); ylabel('x[n]'); grid on

8.5 Señales reconstruidas: stairs

Hay una función llamada stairs(n, x) que realmente no representa una señal discreta, sino una señal continua reconstruida a partir de las muestras: dibuja una escalera que mantiene cada valor constante hasta la siguiente muestra. Esto es exactamente lo que hace un conversor digital-analógico (D/A) conocido como retenedor de orden cero (ZOH), que estudiaremos en la asignatura.

Para verlo con claridad, dibujamos juntas las muestras (stem) y su reconstrucción en escalera (stairs):

clear all; close all; clc;
n = 0:8;                     % índices de muestra
x = sin(2*pi*n/8);           % muestras de la señal

stairs(n, x, 'LineWidth', 1.5)    % reconstrucción por retenedor de orden cero
hold on
plot(n, x, 'o', 'LineWidth', 1.2)  % las muestras originales
hold off
title('Reconstrucción por retenedor de orden cero (stairs)')
xlabel('n'); ylabel('Amplitud'); grid on
legend('stairs (reconstruida)', 'plot (muestras)')

9 Crear tus propias funciones (ficheros .m)

Hasta ahora hemos usado funciones que MATLAB ya trae hechas (sin, plot, linspace…). Pero también puedes escribir las tuyas propias para reutilizar un cálculo tantas veces como quieras sin repetir código. En MATLAB, una función vive en su propio archivo .m y tiene esta estructura:

function [salida1, salida2, ...] = nombreFuncion(entrada1, entrada2, ...)
    % cuerpo: usa las entradas y calcula las salidas
end

Reglas importantes:

  • El archivo debe llamarse exactamente igual que la función, con extensión .m. Si la función se llama senoide, el archivo tiene que ser senoide.m.
  • El archivo debe estar en la carpeta actual de MATLAB (o en una carpeta del path). Si no, MATLAB no la encuentra y da error.
  • Una función tiene su propio espacio de trabajo: las variables que crea dentro son locales y no aparecen en tu Workspace. Solo “salen” los valores que asignes a las variables de salida.
  • Se llama igual que cualquier función de MATLAB: y = senoide(2, 3, t);.
NotaScript vs. función

Un script (lo que venimos escribiendo) es una lista de órdenes que se ejecutan tal cual y comparten el Workspace global. Una función encapsula un cálculo con entradas y salidas bien definidas y con variables locales. Usa funciones cuando vayas a repetir una misma operación con datos distintos.

Vamos a ver un ejemplo muy sencillo en el que creamos una función que genera una senoide. Crea un archivo nuevo llamado senoide.m con este contenido y guárdalo en la misma carpeta que tu script:

function y = senoide(A, f, t)
    % SENOIDE  Genera una senoide muestreada.
    %   y = senoide(A, f, t) devuelve A*sin(2*pi*f*t) evaluada en el vector t,
    %   con amplitud A y frecuencia f (en Hz).
    y = A * sin(2*pi*f*t);
end

Desde tu script ya puedes usarla cuantas veces quieras con distintos valores, sin volver a escribir la fórmula:

AdvertenciaFunciones locales

En los ejemplos de este documento la función aparece al final del mismo script (como función local) para que cada ejemplo sea autocontenido. MATLAB también admite esta forma, pero una función local solo puede usarse desde ese script. Si la guardas en su propio archivo senoide.m, podrás usarla desde cualquier script de la carpeta. Elige una de las dos opciones: si la función está en ambos sitios, MATLAB usa la local. En ambos casos, la función local debe ir siempre al final del script, y el script no puede llamarse igual que la función.

clear all; close all; clc;
t = linspace(0, 1, 500);

y1 = senoide(2, 3, t);      % amplitud 2, frecuencia 3 Hz
y2 = senoide(1, 5, t);      % amplitud 1, frecuencia 5 Hz

plot(t, y1, 'b-', 'LineWidth', 2); hold on
plot(t, y2, 'r-', 'LineWidth', 2); hold off
xlabel('t (s)'); ylabel('Amplitud'); grid on
title('Dos senoides creadas con nuestra función senoide')
legend('A=2, f=3 Hz', 'A=1, f=5 Hz')

% --- función local (en tu MATLAB iría en un archivo aparte: senoide.m) ---
function y = senoide(A, f, t)
    y = A * sin(2*pi*f*t);
end

Tip✏️ Ejercicio 7

Escribe una función escalon que reciba un vector de tiempos t y un instante t0, y devuelva el escalón unitario desplazado: \[ u(t - t_0) = \begin{cases} 1, & \text{si } t \ge t_0 \\ 0, & \text{si } t < t_0 \end{cases} \] Represéntalo para \(t \in [-2, 5]\) (700 puntos) con \(t_0 = 1\), con una línea azul continua de grosor 2 y el eje vertical entre \(-0.2\) y \(1.2\).

Contenido del archivo escalon.m:

function y = escalon(t, t0)
    % ESCALON  Escalón unitario desplazado u(t - t0).
    %   y = escalon(t, t0) vale 1 cuando t >= t0 y 0 en el resto.
    y = double(t >= t0);   % la comparación t>=t0 da 1 (verdadero) o 0 (falso)

    % Implementación alternativa, menos eficiente:
    % y = zeros(size(t));  % vector de ceros del mismo tamaño que t
    % for i = 1:length(t)  % recorre todos los elementos de t
    %    if t(i) >= t0    % si t >= t0, asignamos 1
    %        y(i) = 1;
    %    else
    %        y(i) = 0;    % de lo contrario, asignamos 0
    %    end
    %end
end

La idea clave es la comparación t >= t0: MATLAB la evalúa elemento a elemento y produce un vector de 1 (donde se cumple) y 0 (donde no); double lo convierte en números para poder dibujarlo. Y el script que la utiliza (de nuevo, con la función al final para que la celda sea autocontenida):

clear all; close all; clc;
t  = linspace(-2, 5, 700);
t0 = 1;                      % desplazamiento del escalón
y  = escalon(t, t0);

plot(t, y, 'b-', 'LineWidth', 2)
xlabel('t (s)'); ylabel('u(t - t_0)'); grid on
ylim([-0.2 1.2])
title('Escalón unitario desplazado t_0 = 1')


% En tu MATLAB esto va en un fichero individual (escalon.m)
function y = escalon(t, t0)
    % ESCALON  Escalón unitario desplazado u(t - t0).
    %   y = escalon(t, t0) vale 1 cuando t >= t0 y 0 en el resto.
    y = double(t >= t0);   % la comparación t>=t0 da 1 (verdadero) o 0 (falso)
end

9.1 Funciones anónimas: el operador @

No siempre compensa crear un archivo .m para una fórmula de una sola línea. Para esos casos MATLAB tiene las funciones anónimas, que se escriben con el operador @ directamente en el script:

clear all; clc;
f = @(x) x.^2;      % f es una funcion que eleva al cuadrado
f(3)                % se llama igual que cualquier otra funcion
f([1 2 3])          % y sigue funcionando elemento a elemento
ans = 9
ans = 1x3 double
     1     4     9

Entre paréntesis, detrás de la @, van los argumentos de entrada; después, la expresión que se calcula. La función anónima puede usar variables que ya existan en el script, y se queda con el valor que tenían en el momento de crearla:

clear all; clc;
A = 2;
g = @(t) A*sin(2*pi*t);   % g captura el valor A = 2
A = 100;                  % cambiar A ahora no afecta a g
g(0.25)                   % sigue valiendo 2, no 100
ans = 2

Lo que hace @ realmente es crear un handle: una variable que contiene una función en lugar de un número. Y como es una variable normal, se puede pasar como argumento a otra función:

clear all; clc;
h = @(x) exp(-x);
t = linspace(0, 3, 200);

plot(t, h(t), 'LineWidth', 2)      % h se evalua como cualquier funcion
xlabel('t'); ylabel('h(t)'); grid on
title('Funcion anonima h(t) = e^{-t}')

Nota

Esto será imprescindible en la Práctica 2: los resolvedores de ecuaciones diferenciales de MATLAB reciben la ecuación como un handle, no como un archivo.

Tip✏️ Ejercicio 8

Crea con @ una función anónima p que calcule \(p(x) = 3x^2 - 2x + 1\). Evalúala en \(x = 2\) y represéntala para \(x \in [-2, 3]\) (300 puntos) con una línea azul continua de grosor 2.

clear all; close all; clc;
p = @(x) 3*x.^2 - 2*x + 1;

p(2)                          % 3*4 - 2*2 + 1 = 9

x = linspace(-2, 3, 300);
plot(x, p(x), 'b-', 'LineWidth', 2)
xlabel('x'); ylabel('p(x)'); grid on
title('p(x) = 3x^2 - 2x + 1')
ans = 9

Fíjate en el .^ y en que no ha hecho falta crear ningún archivo: para una fórmula corta como esta, la función anónima es más cómoda que un .m.

10 Ejercicios propuestos

Estos ejercicios repasan lo visto en la práctica, de menor a mayor dificultad. Intenta resolverlos por tu cuenta antes de desplegar la solución.

Tip✏️ Ejercicio 9

Usando el operador :, crea un vector x con todos los números impares del 1 al 19. Muestra cuántos elementos tiene (length), su primer y último valor (con x(1) y x(end)) y los elementos que ocupan una posición par dentro del vector.

x = 1x10 double
     1     3     5     7     9    11    13    15    17    19
n = 10
primero = 1
ultimo = 19
pares = 1x5 double
     3     7    11    15    19
Tip✏️ Ejercicio 10

Define t con 500 puntos entre 0 y 2 s y representa la señal amortiguada \(x(t) = e^{-t}\,\sin(2\pi\cdot 3\cdot t)\) con una línea azul continua de grosor 2. Añade etiquetas a los ejes, un título y la rejilla.

Tip✏️ Ejercicio 11

Dada la señal \(x(t) = \cos(2\pi\cdot 2\cdot t)\) en \([0, 2]\) s, dibuja en una figura de 3×1 (de arriba abajo), usando 500 puntos y líneas continuas de grosor 1.5: la señal \(x(t)\) en azul, su valor absoluto \(|x(t)|\) en rojo y su cuadrado \(x(t)^2\) en negro.

Tip✏️ Ejercicio 12

Escribe una función pulso(t, A, dur, retardo) que genere un pulso cuadrado de amplitud A que valga A en el intervalo retardo ≤ t < retardo+dur y 0 fuera. Represéntalo para A = 2, dur = 1.5, retardo = 1, con \(t \in [0, 5]\) (1000 puntos), una línea azul continua de grosor 2 y el eje vertical entre \(-0.5\) y \(2.5\).

Reutilizamos la idea del escalón: una comparación lógica sobre el vector t produce 1 donde se cumple y 0 donde no. (En tu MATLAB, pulso iría en su propio archivo pulso.m; aquí la ponemos como función local al final de la celda.)

clear all; close all; clc;
t = linspace(0, 5, 1000);
y = pulso(t, 2, 1.5, 1);        % amplitud 2, duración 1.5, retardo 1

plot(t, y, 'b-', 'LineWidth', 2)
xlabel('t (s)'); ylabel('y(t)'); grid on
ylim([-0.5 2.5])
title('Pulso cuadrado: A = 2, dur = 1.5, retardo = 1')

function y = pulso(t, A, dur, retardo)
    % Pulso cuadrado de amplitud A en [retardo, retardo+dur)
    y = A * ((t >= retardo) & (t < retardo + dur));
end

Tip✏️ Ejercicio 13

Genera una onda cuadrada \(x = A\,\texttt{square}(2\pi f t)\) con A = 3 y f = 5 Hz. Calcula su potencia media con sum y comprueba que coincide con el valor teórico \(A^2\) (una onda cuadrada vale siempre \(\pm A\), así que su potencia es \(A^2\)).

Nota: la función square pertenece a la Signal Processing Toolbox. Si al ejecutarla MATLAB indica que no la encuentra, instala esa toolbox (en MATLAB Online ya viene incluida).

P = 9
Pteorica = 9
Tip✏️ Ejercicio 14

Muestrea \(x(t) = \sin(2\pi\cdot 2\cdot t)\) en \([0, 1]\) s a una frecuencia de muestreo fs = 10. En una misma figura, superpón: la señal continua (plot con 1000 puntos, línea azul continua de grosor 1.2), su reconstrucción por retenedor de orden cero (stairs, línea verde continua de grosor 1.5) y sus muestras (stem en rojo con círculos rellenos y grosor 1.2). Añade una leyenda.

Tip✏️ Ejercicio 15

Genera un pulso discreto p[n] de 5 muestras a 1 (con la función pulso de antes) y una exponencial \(h[n] = e^{-0.3 n}\), ambos para \(n = 0, 1, \dots, 20\). Calcula la convolución y = conv(p, h) y dibuja las tres señales (p, h, y) con stem (color azul y círculos rellenos) en una figura de 3×1. ¿Qué longitud tiene y?

La convolución tiene length(p) + length(h) - 1 = 21 + 21 - 1 = 41 muestras.

Tip✏️ Ejercicio 16

Crea una señal ruidosa \(x[n] = \sin(2\pi n/50) + 0.3\,r[n]\) para \(n = 0, 1, \dots, 200\), donde \(r[n]\) es ruido aleatorio generado con randn(size(n)) (usa rng(0) para que sea reproducible). Suavízala con una media móvil de M = 9 puntos mediante convolución (conv(x, ones(1,M)/M, 'same')) y dibuja superpuestas la señal original (línea continua gris claro de grosor 0.5, color [0.7 0.7 0.7]) y la suavizada (línea azul continua de grosor 2). Añade una leyenda.

Convolucionar con ones(1,M)/M promedia cada muestra con sus vecinas: es un filtro de media móvil que atenúa el ruido. La opción 'same' mantiene la longitud original.

Tip✏️ Ejercicio 17

Escribe una función con dos salidas [E, P] = energia_potencia(x) que devuelva la energía \(E = \sum_n |x[n]|^2\) y la potencia media \(P = E/N\) de una señal. Aplícala a tres señales de 1000 muestras (una senoide, un pulso y una onda cuadrada) e imprime sus energías y potencias.

Senoide : E = 499.50, P = 0.4995
Pulso   : E = 500.00, P = 0.5000
Cuadrada: E = 1000.00, P = 1.0000
Tip✏️ Ejercicio 18

Una onda cuadrada se puede aproximar sumando senoides de frecuencias impares: \[ x(t) \approx \frac{4}{\pi}\sum_{k=1,3,5,\dots} \frac{\sin(2\pi k f t)}{k} \] Calcula esta aproximación para f = 1 Hz sumando los términos con \(k\) impar hasta \(k_{\max} = 1, 3, 9\) y \(49\) (es decir, 1, 2, 5 y 25 senoides) y dibuja las cuatro aproximaciones en una figura de 2×2, superponiendo en cada una la onda cuadrada objetivo (square). Usa 1000 puntos en \(t \in [0, 2]\), una línea azul continua de grosor 1.2 para la aproximación, una roja discontinua de grosor 0.5 para la onda cuadrada y el eje vertical entre \(-1.5\) y \(1.5\). Observa cómo mejora al añadir armónicos (y el pico que queda en los saltos: el fenómeno de Gibbs).

Al sumar más armónicos la aproximación se parece cada vez más a la onda cuadrada.

Notas

  1. Sea una señal \(x(t) = A \sin(2\pi f t)\), su potencia media en un periodo \(T = 1/f\) es: \[P = \frac{1}{T}\int_0^T x^2(t)\, dt = \frac{1}{T}\int_0^T A^2 \sin^2(2\pi f t)\, dt\] Usando la identidad trigonométrica \(\sin^2(\theta) = \frac{1 - \cos(2\theta)}{2}\): \[P = \frac{A^2}{T}\int_0^T \frac{1 - \cos(4\pi f t)}{2}\, dt = \frac{A^2}{2T}\left[t - \frac{\sin(4\pi f t)}{4\pi f}\right]_0^T\] Como \(\sin(4\pi f T) = \sin(4\pi) = 0\), dicho término se anula y queda que \[P = \frac{A^2}{2T} \cdot T = \frac{A^2}{2}\]↩︎