Diseño de Filtros Digitales - Ingenieros Eléctricos y Electrónicos, SA

Transcripción

Diseño de Filtros Digitales - Ingenieros Eléctricos y Electrónicos, SA
UNIVERSIDAD DE PANAM Á
ESCUELA DE ELECTRÓNICA
TRATAMIENTO DIGITAL DE LA INFORMACIÓN
PROGRAMAS PARA EL DISEÑO
E IMPLEMENTACIÓN DE FILTROS DIGITALES
EN EL SHARC EZ-KIT
(IMPLEMENTACIÓN EN LENGUAJE-C)
RICARDO LAMBRAÑO
23/07/98
CONTENIDO
1
2
3
4
5
6
7
8
INTRODUCCIÓN.......................................................................................................................................... 3
PROCESAMIENTO DIGITAL DE SEÑALES ............................................................................................. 3
FILTROS DE RESPUESTA FINITA (FIR)................................................................................................... 5
3.1
TEORÍA .......................................................................................................................................... 5
3.1.1
FILTRO PASA BAJOS............................................................................................................ 5
3.1.2
FILTRO PASA ALTOS............................................................................................................ 6
3.1.3
FILTRO PASA BANDA........................................................................................................... 6
3.1.4
FILTRO RECHAZA BANDA.................................................................................................... 6
3.1.5
DIFERENCIADOR................................................................................................................... 6
3.1.6
TRANSFORMADA HILBERT.................................................................................................. 7
3.1.7
VENTANAS............................................................................................................................. 7
3.2
PROGRAMA DE DISEÑO .............................................................................................................. 8
3.3
PROGRAMA DE IMPLEMENTACIÓN ........................................................................................... 9
FILTROS DE RESPUESTA INFINITA (IIR) .............................................................................................. 11
4.1
TEORÍA ........................................................................................................................................ 12
4.1.1
FILTRO PASA BAJOS.......................................................................................................... 12
4.1.3
FILTRO PASA BANDA......................................................................................................... 13
4.1.4
FILTRO RECHAZA BANDA.................................................................................................. 14
4.1.5
FILTRO NOTCH.................................................................................................................... 15
4.2
PROGRAMA DE DISEÑO ............................................................................................................ 15
4.3
PROGRAMA DE IMPLEMENTACIÓN ......................................................................................... 16
LISTADO DE PROGRAMAS DE DISEÑO ............................................................................................... 18
5.1
FILTROS DE RESPUESTA FINITA (FIR)..................................................................................... 18
5.2
FILTROS DE RESPUESTA INFINITA (IIR) .................................................................................. 26
LISTADO DE PROGRAMAS DE IMPLEMENTACIÓN............................................................................ 38
6.1
FILTROS DE RESPUESTA FINITA (FIR)..................................................................................... 38
6.2
FILTROS DE RESPUESTA INFINITA (IIR) .................................................................................. 42
RECOMENDACIONES Y MEJORAS ....................................................................................................... 48
BIBLIOGRAFÍA.......................................................................................................................................... 48
3
1
INTRODUCCIÓN
El objetivo de este trabajo es el de servir como explicación a la forma en que fueron escritos los
programas de diseño y utilización de los filtros digitales de coeficientes constantes invariantes en el tiempo.
Como indica el titulo de este trabajo "PROGRAMAS PARA EL DISEÑO E IMPLEMENTACIÓN DE
FILTROS DIGITALES...", en el mismo no solo nos dedicaremos al diseño de los filtros, sino a su
1
implementación en un sistema de DSP .
El sistema que hemos seleccionado para la implementación de los filtros esta basado en el
microprocesador de DSP de la compañía Analog Devices el ADSP-21061. El ADSP-21061 es un
2
microprocesador de 32bits punto flotante optimizado para DSP. El mismo es el núcleo de un sistema de
evaluación llamado el ADSP21061 SHARC EZ-KIT, este sistema fue utilizado por ser el sistema de
desarrollo con que cuenta la Universidad Tecnológica de Panamá, para este curso. En la tabla 1,
mostramos un resumen de las características del sistema.
Tabla 1. Características del ADSP21061 SHARC EZ-KIT.
Parámetro
ADSP-21061 SHARC EZ-KIT
MFLOPS
40(normal)/120(máximo)
Memoria de Datos
512Kbit (16K x 32bits)
Memoria de Programa
512Kbit (11K x 48bits)
Registros
16 Punto Flotante
Frec. Muestreo
Hasta 48KHz
Canales A/D
2 x 16bits
Canales D/A
2 x 16bits
Simulador
Sí
Documentación
Extensa
Ensamblador
Sí
Compilador C
Sí
Por último, este documento asume un conocimiento previo de: Teorema de Nyquist; los
dispositivos necesarios para la conversión análoga a digital y digital a análoga; concepto de
frecuencia digital; conocimiento de la Transformada-Z; y diseño de filtros digitales.
2
PROCESAMIENTO DIGITAL DE SEÑALES
1
DSP = Digital Signal Processing
2
Los microprocesadores optimizados para DSP, cuentan con
unidades físicas de multiplicación y acumulación, las cuales
permiten realizar una multiplicación y acumulación (Sumatoria) en
un ciclo de reloj.
4
El Procesamiento Digital de Señales, DSP por sus siglas en Inglés, es un área interdiciplinaria de la
electrónica, matemáticas e informática que permite realizar cualquier función de dispositivos
electrónicos analógicos y hasta funciones que no pueden ser realizadas con dispositivos
analógicos, en un sistema digital, normalmente basado en microprocesadores.
Con la utilización de DSP se puede realizar cualquier labor de una manera más precisa, ya que
los componentes no requieren ajustes y no cambian sus características con el tiempo.
Tabla 2.
Áreas de utilización de DSPs
Audio
Ecualizadores Gráficos; Analizadores de Espectro; compresión de
audio (MiniDisc); Discos Compactos; Efectos Digitales; Sistemas de
Teatro Casero (THX); reducción de ruido (Dolby); etc.
Automóviles
Frenos antibloqueo; Bolsas de Aire; Supresión de ruido activo;
Control de mezcla de combustible; etc.
Computadoras
MODEMs; tarjetas de sonido; tarjetas de video; sintetización y
reconocimiento de voz; etc.
Comunicaciones
Compresión de voz; eliminación de eco; ecualización adaptativa;
multiplexión de canales; modulaciones digitales; etc.
Medicina
Eliminación de ruido en ECG, EEG, VCG, EMG; análisis fisiológico;
mejoramiento de imágenes CT, MR, X-RAY; etc.
Militar
Guía de misiles; GPS; Detección de agentes químicos; RADAR;
SONAR; Visión Nocturna; etc.
De hecho, consideramos que en un período de aproximadamente diez años, todos los sistemas
electrónicos serán basados en el procesamiento digital de señales, dejando campo para la electrónica
analógica son en las tareas de amplificación de potencia, amplificación de bajas señales y mezcla de
frecuencia para transmisión y recepción, o sea la preparación de la señal para ser procesada o después
de ser procesada.
Aunque para las aplicaciones antes mencionadas se utilizan algoritmos o herramientas tales como
la Transformada Rápida de Fourier (FFT), Filtros adaptivos, Filtros Pareados (Matched Filters), Filtros
Pareados Adaptivos, Correlación, Convolución, Transformadas Discretas de Coseno, Transformadas de
Hilbert, Diferenciadores, Integradores, mezcladores, Algoritmos de Vitervi, Codificadores cíclicos,
Codificadores Convolucionales, Decodificadores Trellis, etc. También se utilizan filtros de coeficientes
constantes.
Existen dos tipos de filtros de coeficientes constantes, implementables en el dominio del tiempo,
3
4
estos son los Filtros de Respuesta Finita (FIR ) y los Filtros de Respuesta Infinita (IIR ).
3
FIR = Finite Impulse Response
4
IIR = Infinite Impulse Response
5
3
FILTROS DE RESPUESTA FINITA (FIR)
Los filtros FIR, también conocidos como Filtros Transversales (Transversal Filters) o como Sistemas
de Average Móvil (Moving Average Systems {MA}), son los filtros más sencillos de diseñar e implementar.
Son llamados de respuesta finita, porque el proceso de filtrado se realiza por medio de la
convolución de la señal de entrada, con la respuesta al impulso del filtro. Como la respuesta al impulso es
un número finito de coeficientes, el filtro es llamado "Filtro de Respuesta Finita".
N -1
H(z) = ∑ bn z -n
(Ec. 1)
n=0
La función de transferencia de los filtros FIR esta dada por (1), como se puede observar este filtro
solo cuenta con ceros y ningún polo, lo que nos garantiza la estabilidad del mismo.
Es importante destacar que los filtros FIR nos brindan las siguientes ventajas sobre los filtros IIR:
estabilidad garantizada; fase lineal; poca sensibilidad a los efectos del tamaño finito de la palabra
(número de bits); fácil de diseñar; y fácil de implementar.
La única desventaja de los filtros FIR en comparación a los filtros IIR, es el gran número de
coeficientes que se necesitan para obtener las mismas características de ancho de banda de transición y
atenuación en la banda de rechazo.
3.1
TEORÍA
El procedimiento de diseño de los filtros FIR utilizado, fue el método de ventanas. En este método
se obtienen los coeficientes del filtro ideal y se multiplica o ponderan por el valor de una función que
llamaremos función de ventana.
En la ecuación (2), se muestra el calculo de los coeficientes del filtro.
h(n) = hd (n)w(n)
para - N/2 ≤ n ≤ N/2
(Ec. 2)
Donde h(n) son los coeficientes del filtro; hd(n) son los coeficientes del filtro ideal; y w(n) son los
coeficientes de las ventanas.
3.1.1
FILTRO PASA BAJOS
hd (n) =
sin(n ω c )
nπ
para n ≠ 0
(Ec. 3)
hd (0) =
ωc
π
para n = 0
6
3.1.2
FILTRO PASA ALTOS
hd (n) = -
sin(n ω c )
nπ
para n ≠ 0
(Ec. 4)
hd (0) = 1 -
3.1.3
ωc
π
para n = 0
FILTRO PASA BANDA
h d (n) =
sin(n ω c2 ) - sin(n ω c1 )
nπ
para n ≠ 0
(Ec. 5)
hd (0) =
3.1.4
ω c2 - ω c1
π
para n = 0
FILTRO RECHAZA BANDA
hd (n) = -
sin(n ω c2 ) - sin(n ω c1 )
nπ
para n ≠ 0
(Ec. 6)
hd (0) = 1 -
3.1.5
ω c2 - ω c1
π
para n = 0
DIFERENCIADOR
hd (n) =
cos(nπ ) sin(nπ )
n
π k2
para n ≠ 0
(Ec. 7)
hd (0) = 0
para n = 0
7
3.1.6
TRANSFORMADA HILBERT
hd (n) =
1 - cos(nπ )
nπ
para n ≠ 0
(Ec. 8)
hd (0) = 0
para n = 0
El diferenciador tiene una respuesta de frecuencia H(w) = jw, definida en todo el intervalo de
Nyquist. El transformador de Hilbert tiene una respuesta de frecuencia H(w) = -j sign(w). Ambos tienen alta
importancia en algoritmos de comunicaciones.
3.1.7
VENTANAS
Las ventanas, desde el punto de vista de los ingenieros electrónicos, nos sirven para suavisar el
paso entre los primeros coeficientes del filtro (ambos extremos) y cero, como sabemos cualquier paso
abrupto crea distorsiones armónicas.
Desde el punto de vista de los matemáticos (correcto), nos sirven para convolucionar el espectro del
filtro con el espectro de una señal mejor que la del sin(x)/(x) (espectro de ventana rectangular).
Ventana Rectangular
w(n) = 1
(Ec. 9)
Ventana Hanning
w(n) = 0.5 + 0.5 cos(
2πn
)
N
(Ec. 10)
Ventana Hamming
w(n) = 0.54 + 0.46 cos(
2πn
)
N
(Ec. 11)
Ventana Blackman
w(n) = 0.42 + 0.5 cos(
2πn
4πn
) + 0.08 cos(
)
N
N
En la tabla 3, se muestran las características de las ventanas antes mencionadas.
(Ec. 12)
8
Ventana
Transición
(Hz)
Rizo
(db)
Relación
(db)
Atenuación
(db)
Rectangular
0.9/N
0.7416
13
21
Hanning
3.1/N
0.0546
31
44
Hamming
3.3/N
0.0194
41
53
Blackman
5.5/N
0.0017
57
74
Tabla 3. Características de distintas ventanas
3.2
PROGRAMA DE DISEÑO
El programa de diseño de filtros FIR, fue escrito en Lenguaje C, compilado con el Compilador de C
de Microsoft Versión 6.00.
Para ejecutar el mismo, el usuario debe escribir el nombre del programa FIRF.EXE desde la línea
de comando de MS-DOS y presionar [Enter].
El programa pregunta que tipo de filtro o función desea calcular, el número de puntos, tipo de
ventana, frecuencia de muestreo y frecuencias de corte en caso de ser un filtro lo seleccionado, figura 1.
Programa Para el Diseño de Filtros FIR
Versión 2.00 de 23/07/98 R. Lambraño
1-Pasa Bajos
2-Pasa Altos
3-Pasa Banda
4-Rechaza Banda
5-Differenciador
6-Transformada de Hilbert
Seleccione Tipo de Filtro?
Numero de Puntos?
Frecuencia de Muestreo (KHz)?
Frecuencia de Corte (Hz)?
Ventana
1-Rectangular
2-Hanning
3-Hamming
4-Blackman
1
255
48
1000
Transicion
0.9/N
3.1/N
3.3/N
5.5/N
Rizo (db)
0.7416
0.0546
0.0194
0.0017
Seleccione Tipo de Ventana? 1
Figura 1. Pantalla de Ejecución de Programa FIRF.EXE
Relacion
13db
31db
41db
57db
Aten.
21db
44db
53db
74db
9
El diseño brindado por el programa no contempla el calculo del número de coeficientes y tipo de
ventana, según sean las necesidades del filtro.
Esto tiene dos razones importantes de ser. Primero el objetivo del programa es principalmente para
uso didáctico, de tal forma que el estudiante pueda evaluar las distintas respuestas con diferentes números
de coeficientes y tipos de ventana.
Segundo, en la utilización real de filtros digitales uno cuenta con una cantidad definida de
coeficientes que puede utilizar (por restricciones de tiempo), de tal forma que se deja al diseñador la libertad
de controlar todos los parámetros del filtro y es responsabilidad del diseñador crear el mejor diseño con las
limitantes presentes en el proyecto.
La salida del programa es un archivo llamado FIR.DAT, este archivo tiene los coeficientes en
formato de punto flotante y como comentarios, separados por "//", las especificaciones del filtro así como los
valores de los coeficientes, en la figura 2 se muestra un extracto del archivo FIR.DAT.
// Filtro: LP
//Ventana: Rectangular
//
Taps: 255
//
Fs:
48.000KHz
//
Fc: 1000.000Hz
-0.0019884426097825,
-0.0017863418970508,
-0.0015501982493486,
-0.0012835076054713,
...
-0.0035751597654873,
-0.0037127847174568,
-0.0037894034069617,
-0.0038022494094094,
-0.0037495578027593,
//
//
//
//
h(-127)
h(-126)
h(-125)
h(-124)
=
=
=
=
-1.9884426E-003
-1.7863419E-003
-1.5501982E-003
-1.2835076E-003
//
//
//
//
//
h(-086)
h(-085)
h(-084)
h(-083)
h(-082)
=
=
=
=
=
-3.5751598E-003
-3.7127847E-003
-3.7894034E-003
-3.8022494E-003
-3.7495578E-003
Figura 2. Extracto de archivo FIR.DAT creado por FIR.EXE
3.3
PROGRAMA DE IMPLEMENTACIÓN
Como habíamos anunciado anteriormente, la implementación de los filtros será realizada en el
microprocesador de Analog Devices, el ADSP-21061.
El ADSP-21061 tiene un compilador de Lenguaje C, por lo que la implementación del filtro es muy
sencilla. No es necesario mostrar el listado completo del programa en esta sección (ver punto 10), así que
nos concentraremos, solo en las secciones importantes del filtro.
#define TAPS 255
float pm H[TAPS] =
{
#include "fir.dat"
};
float dm X[TAPS+1];
// Numero de Coeficientes (Orden del Filtro)
// Coeficientes del Filtros leidos del
// Archivo ASCII <FIR.DAT>
// Buffer del Filtro
10
Lo primero que siempre se declara en cualquier programa, son las variables y constantes. Para el
programa FIR.C, se declara la constante global TAPS 255, esta constante tiene el número de coeficientes o
derivaciones del filtro.
Con el valor de esta constante, se "dimensionan" los buffers o arreglos donde se almacenaran los
coeficientes del filtro (H[TAPS]), y los estados anteriores de x(n) (X[TAPS]).
La sentencia float dm X[TAPS+1], define a la etiqueta o arreglo X[], como una variable, en memoria
de datos.
La sentencia float pm H[TAPS], define a la etiqueta o arreglo H[], como una variable (aunque será
utilizada como constantes), en memoria de programa.
Existen una razón importantísima para haber declarado ambos arreglos en distintas áreas de
memoria (DATA y PROGRAMA), y es porque este microprocesador puede leer a la vez ambas
direcciones de memoria.
La sentencia #include "FIR.DAT" es lo que le indica al compilador que lea el archivo creado por el
programa FIRF.EXE y lo almacene o inicialize el buffer H[], a estos valores.
/***************************************************************************
*
*
Rutina de Interrupción del CODEC (Recepción)
*
Aquí se realiza el Filtrado!
*
***************************************************************************/
void SPORT0_RX( int sig_num )
{
float ACUM;
int
k,FILT;
ACUM = rx_buf[1];
// Señal de Entra del CODEC (L)
if (ON_OFF)
// Bandera de Funcionamiento
{
X[TAPS] = ACUM;
// X[n] = Ultimo elemento del buffer
ACUM = 0.0;
// Blanquear Acumulador
for(k=0;k<TAPS;k++) // Ciclo de K = 0 a TAPS
{
ACUM = ACUM + H[k]*X[k]; // "Convolución"
X[k] = X[k+1];
// Corrimiento de Datos
}
}
tx_buf[1] = ACUM;
tx_buf[2] = ACUM;
}
// Señal de Salida del CODEC (L)
// Señal de Salida del CODEC (R)
La rutina SPORT0_RX, es una rutina de interrupción. Esta rutina es llamada cada vez que los
convertidores realizan un muestreo. De hecho, en el Procesamiento Digital de Señales en tiempo real, todo
el tratamiento de la información tiene que realizarse entre muestra y muestra.
Por ejemplo en este microprocesador muestreando a 48KHz, podemos ejecutar una rutina de
interrupción con un máximo de 833 instrucciones o ciclos de máquina (40MHz/48KHz=833.3).
11
La primera línea ACUM = rx_buf[1] lee el ADC izquierdo y escribe este valor en la variable ACUM.
La línea if (ON_OFF), prueba el estado de una variable para verificar si el usuario desea el filtro encendido o
apagado.
X[TAPS] = ACUM;
ACUM = 0.0;
for(k=0;k<TAPS;k++)
{
ACUM = ACUM + H[k]*X[k];
X[k] = X[k+1];
}
Si el usuario desea el filtro encendido, entonces se entrega el valor leido en ACUM al elemento
TAPS del buffer X[], se blanquea la variable ACUM y se realiza la sumatoria de convolución, entre los
coeficientes del filtro H[] y las entradas anteriores X[].
tx_buf[1] = ACUM;
tx_buf[2] = ACUM;
// Señal de Salida del CODEC (L)
// Señal de Salida del CODEC (R)
Por último se escriben ambos canales del DAC con el valor de la variable ACUM, si el filtro estaba
apagado, este será el valor leido del ADC. Si el filtro estaba encendido, esta variable contiene el valor
filtrado.
4
FILTROS DE RESPUESTA INFINITA (IIR)
Los filtros IIR, también conocidos como Sistemas Auto-regresivos (Auto-Regresive {AR}), son
llamados de respuesta infinita, porque el proceso de filtrado se realiza por medio de la evaluación de la
ecuación de diferencias que regulan el sistema. Como la ecuación de diferencias depende de las salidas
anteriores del filtro, existe una dependencia de los infinitos estados anteriores de la variable de
salida a la variable de salida actual, por tal razón son llamados de Respuesta al Impulso Infinita.
N -1
H(z) =
∑b
k
z
-k
k =0
M -1
1+ ∑ a k z
(Ec. 13)
-k
k =0
La función de transferencia de los filtros IIR esta dada por (13), como se puede observar este filtro
cuenta con ceros y polos, por lo que la estabilidad del mismo, no esta garantizada.
Es importante destacar que los filtros IIR nos brindan una gran ventaja sobre los filtros FIR, esta es
la pequeña cantidad de coeficientes que se requieren para obtener una gran atenuación y estrecha
banda de transición.
Las desventajas de los filtros IIR en comparación a los filtros FIR son: Problemas de
estabilidad; complejidad en el diseño; sensibilidad a tamaño finito de palabras; y fase no lineal.
12
4.1
TEORÍA
El procedimiento de diseño de los filtros IIR utilizado, fue el método de transformada bilineal. En
este método se obtienen los coeficientes del filtro analógico y se aplica una transformación de variables para
obtener los coeficientes del filtro digital.
Esta transformación de variables es conocida como la Transformación Bilineal, ecuación (14).
s=
1 - z -1
1 + z -1
(Ec. 14)
Además de la transformación de la variable (s->z), es importante también ajustar las frecuencias del
plano de Laplace al plano Z, este ajuste (no lineal), es conocido en Inglés como Frequency Prewarping,
ecuación (15).
Ω = tan( ω/2)
(Ec. 15)
Con esta información y nuestros conocimientos previos de diseño de filtros analógicos, podemos
5
modificar las formulas de diseño de Filtros Butterworth para diseñar directamente filtros digitales.
Debemos aclarar antes de continuar, que para mejorar la sensibilidad de los filtros IIR a los
efectos de la palabra finita dentro del microprocesador, la implementación de los mismos es mejor
realizada como la cascada de varias secciones bi-cuadraticas, ecuación (16).
-1
-2
+
+
H(z) = B0 B1 z-1 B 2 z- 2
1 + A1 z + A2 z
(Ec. 16)
El ángulo de los polos de la función de transferencia de los filtros Butterworth viene dado por la
ecuación (17), donde N es el orden del filtro.
Φi =
4.1.1
π
(N - 1 + 2i)
2N
para i = 1,2,...N
(Ec. 17)
FILTRO PASA BAJOS
Ω0 = tan(
ω0 )
2
(Ec. 18)
2
-1
Gi (1 + z )
H i (z) =
1 + ai1 z -1 + ai2 z - 2
(Ec. 19)
Gi =
Ω0
1 - 2 Ω0 cos Φi + Ω02
(Ec. 20)
ai1 =
2( Ω02 - 1)
1 - 2 Ω0 cos Φi + Ω02
(Ec. 21)
2
5
Valido también para otros tipos de filtro.
13
ai2 =
4.1.2
1 + 2 Ω0 cos Φi + Ω02
1 - 2 Ω0 cos Φi + Ω02
(Ec. 22)
FILTRO PASA ALTOS
Ω0 = - cot(
ω0 )
2
(Ec. 23)
2
-1
G i (1 - z )
H i (z) =
1 + ai1 z -1 + ai2 z - 2
(Ec. 24)
Gi =
Ω0
1 - 2 Ω0 cos Φi + Ω02
(Ec. 25)
ai1 =
2( Ω02 - 1)
1 - 2 Ω0 cos Φi + Ω02
(Ec. 26)
ai2 =
1 + 2 Ω0 cos Φi + Ω02
1 - 2 Ω0 cos Φi + Ω02
(Ec. 27)
sin( ω pa + ω pb )
sin( ω pa ) + sin( ω pb )
(Ec. 28)
c - cos( ω pb )
)
sin( ω pb )
(Ec. 29)
2
4.1.3
FILTRO PASA BANDA
c=
Ω0 = abs(
2
H i (z) =
-2
Gi (1 - z )
1+ ai1 z -1 + ai2 z - 2 + ai3 z -3 + ai4 z -4
(Ec. 30)
Gi =
Ω0
1 - 2 Ω0 cos Φi + Ω02
(Ec. 31)
ai1 =
4c( Ω0 cos Φi - 1)
1 - 2 Ω0 cos Φi + Ω02
(Ec. 32)
2
14
2(2 c2 + 1 - Ω02 )
1 - 2 Ω0 cos Φi + Ω02
(Ec. 33)
4c( Ω0 cos Φi + 1)
1 - 2 Ω0 cos Φi + Ω02
(Ec. 34)
1 + 2 Ω0 cos Φi + Ω02
1 - 2 Ω0 cos Φi + Ω02
(Ec. 35)
sin( ω pa + ω pb )
sin( ω pa ) + sin( ω pb )
(Ec. 36)
ai2 =
ai3 = -
ai4 =
4.1.4
FILTRO RECHAZA BANDA
c=
Ω0 = abs(
sin ω pb
)
cos ω pb - c
-1
-2 2
G
i (1 - 2 cz + z )
H i (z) =
1+ ai1 z -1 + ai2 z - 2 + ai3 z -3 + ai4 z -4
(Ec. 37)
(Ec. 38)
Gi =
Ω0
1 - 2 Ω0 cos Φi + Ω02
(Ec. 39)
ai1 =
4c Ω0 ( cos Φi - Ω0 )
1 - 2 Ω0 cos Φi + Ω02
(Ec. 40)
ai2 =
2(2 c 2 Ω02 + Ω02 - 1)
1 - 2 Ω0 cos Φi + Ω02
(Ec. 41)
ai3 = -
4c Ω0 ( cos Φi + Ω0 )
1 - 2 Ω0 cos Φi + Ω02
(Ec. 42)
1 + 2 Ω0 cos Φi + Ω02
1 - 2 Ω0 cos Φi + Ω02
(Ec. 43)
2
ai4 =
15
4.1.5
FILTRO NOTCH
b=
H(z) =
4.2
1
∆ω
1 + tan(
)
2
1 - 2 cos ω 0 z -1 + z -2
1 - 2b cos ω 0 z -1 + (2b - 1) z - 2
(Ec. 44)
(Ec. 45)
PROGRAMA DE DISEÑO
El programa de diseño de filtros IIR, fue escrito en Lenguaje C, compilado con el Compilador de C
de Microsoft Versión 6.00.
Para ejecutar el mismo, el usuario debe escribir el nombre del programa IIRF.EXE desde la línea de
comando de MS-DOS y presionar [Enter].
El programa pregunta que tipo de filtro desea calcular, el número de secciones bi-cuadráticas,
frecuencia de muestreo y frecuencias de corte, figura 3.
Programa Para el Diseño de Filtros IIR
Versión 2.00 de 23/07/98 R. Lambraño
1-Pasa Bajos
2-Pasa Altos
3-Pasa Banda
4-Rechaza Banda
5-Notch
6-Selectivo
Seleccione Tipo de Filtro?
Numero de Bi-cuadraticas?
Frecuencia de Muestreo (KHz)?
Frecuencia de Corte (Hz)?
1
4
48
1000
Figura 3. Pantalla de Ejecución de Programa IIRF.EXE
El diseño brindado por el programa no contempla el calculo del número de bi-cuadráticas
necesarias, según sean las necesidades del filtro.
16
Esto tiene dos razones importantes de ser. Primero el objetivo del programa es principalmente para
uso didáctico, de tal forma que el estudiante pueda evaluar las distintas respuestas con diferentes números
de bi-cuadráticas.
Segundo, en la utilización real de filtros digitales uno cuenta con una cantidad definida de
coeficientes que puede utilizar (por restricciones de tiempo), de tal forma que se deja al diseñador la libertad
de controlar todos los parámetros del filtro y es responsabilidad del diseñador crear el mejor diseño con las
limitantes presentes en el proyecto.
La salida del programa es un archivo llamado IIR.DAT, este archivo tiene los coeficientes en formato
de punto flotante y como comentarios, separado por "//", las especificaciones del filtro así como los valores
de los coeficientes, en la figura 4 se muestra un extracto del archivo IIR.DAT.
// Filtro: LP
// Polos:
8
//
Fs:
48.000KHz
//
Fc: 1000.000Hz
+0.0041713484408825,
+0.0083426968817649,
+0.0041713484408825,
-0.9503358732908425,
+1.9336504795273130,
+0.0039883483793306,
+0.0079766967586613,
+0.0039883483793306,
-0.8647732333154958,
+1.8488198397981730,
+0.0038587813232834,
+0.0077175626465667,
+0.0038587813232834,
-0.8041934757174437,
+1.7887583504243100,
+0.0037921102995321,
+0.0075842205990642,
+0.0037921102995321,
-0.7730210883769970,
+1.7578526471788690,
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
B2
B1
B0
A2
A1
B2
B1
B0
A2
A1
B2
B1
B0
A2
A1
B2
B1
B0
A2
A1
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
+4.1713484E-003
+8.3426969E-003
+4.1713484E-003
-9.5033587E-001
+1.9336505E+000
+3.9883484E-003
+7.9766968E-003
+3.9883484E-003
-8.6477323E-001
+1.8488198E+000
+3.8587813E-003
+7.7175626E-003
+3.8587813E-003
-8.0419348E-001
+1.7887584E+000
+3.7921103E-003
+7.5842206E-003
+3.7921103E-003
-7.7302109E-001
+1.7578526E+000
Figura 4. Extracto de archivo IIR.DAT creado por IIRF.EXE
4.3
PROGRAMA DE IMPLEMENTACIÓN
Como habíamos anunciado anteriormente, la implementación de los filtros será realizada en el
microprocesador de Analog Devices, el ADSP-21061.
El ADSP-21061 tiene un compilador de Lenguaje C, por lo que la implementación del filtro es muy
sencilla. No es necesario mostrar el listado completo del programa en esta sección (ver punto 10), así que
nos concentraremos, solo en las secciones importantes del filtro.
#define BICUADS 4
float pm H[BICUADS*5] =
{
// Número de Bicuadraticas (Orden del Filtro)
// Coeficientes del Filtros leidos del
// Archivo ASCII <IIR.DAT>
17
#include "iir.dat"
};
float dm X[BICUADS*5];
// Buffer del Filtro X(n) y Y(n)
Lo primero que siempre se declara en cualquier programa, son las variables y constantes. Para el
programa IIR.C, se declara la constante global BICUADS 4, esta constante tiene el número de bicuadraticas
que componen el filtro.
Con el valor de esta constante, se "dimensionan" los buffers o arreglos donde se almacenaran los
coeficientes del filtro (H[BICUADS*5]), y los estados anteriores de x(n) (X[BICUADS*5]).
La sentencia float dm X[], define a la etiqueta o arreglo X[], como una variable, en memoria de
datos.
La sentencia float pm H[], define a la etiqueta o arreglo H[], como una variable (aunque será
utilizada como constantes), en memoria de programa.
Existen una razón importantísima para haber declarado ambos arreglos en distintas áreas de
memoria (DATA y PROGRAMA), y es porque este microprocesador puede leer a la vez ambas
direcciones de memoria.
La sentencia #include "IIR.DAT" es lo que le indica al compilador que lea el archivo creado por el
programa IIRF.EXE y lo almacene o inicialize el buffer H[], a estos valores.
void SPORT0_RX( int sig_num )
{
float ACUM;
int
i,ii,FILT;
ACUM = rx_buf[1];
// Señal de Entra del CODEC (L)
if (ON_OFF)
// Bandera de Funcionamiento
{
for(ii = 0; ii < BICUADS; ii++) // Repetir por N bicuadraticas
{
i = ii*5;
// Ajustar Indice de variables
X[i+2] = ACUM;
// X[n] = Valor del ADC
// o ultimo filtro!
ACUM = H[i+2]*X[i+2];
// Y[n] = B0*X[n]
ACUM = ACUM + H[i+1]*X[i+1];
// Y[n] = Y[n] + B1*X[n-1]
ACUM = ACUM + H[i+0]*X[i+0];
// Y[n] = Y[n] + B2*X[n-2]
ACUM = ACUM + H[i+3]*X[i+3];
// Y[n] = Y[n] + A1*Y[n-1]
ACUM = ACUM + H[i+4]*X[i+4];
// Y[n] = Y[n] + A2*Y[n-2]
X[i+0] = X[i+ 1];
// X[n-2] = X[n-1]
X[i+1] = X[i+ 2];
// X[n-1] = X[n]
X[i+3] = X[i+ 4];
// Y[n-2] = Y[n-1]
X[i+4] = ACUM;
// Y[n-1] = Y[n]
}
}
tx_buf[1] = ACUM;
// Señal de Salida del CODEC (L)
tx_buf[2] = ACUM;
// Señal de Salida del CODEC (R)
}
La rutina SPORT0_RX, es una rutina de interrupción. Esta rutina es llamada cada vez que los
convertidores realizan un muestreo. De hecho, en el Procesamiento Digital de Señales en tiempo real, todo
el tratamiento de la información tiene que realizarse entre muestra y muestra.
18
Por ejemplo en este microprocesador muestreando a 48KHz, podemos ejecutar una rutina de
interrupción con un máximo de 833 instrucciones o ciclos de máquina (40MHz/48KHz=833.3).
La primera línea ACUM = rx_buf[1] lee el ADC izquierdo y escribe este valor en la variable ACUM.
La línea if (ON_OFF), prueba el estado de una variable para verificar si el usuario desea el filtro encendido o
apagado.
Si el usuario desea el filtro encendido, entonces se entrega el valor leido en ACUM al elemento i+2
del buffer X[], se asigna a la variable ACUM la operación que da como resultado B0*X[n] y se realizan las
demás operaciones para obtener finalmente en la variable ACUM = B0*X(n) + B1*X(n-1) + B2*X(n-2) +
A1*Y(n-1) + A2*Y(n-2), que es la función de transferencia de una bicuadratica.
Luego se realiza el corrimiento de datos (X[n-2] = X[n-1], etc.) y se realiza nuevamente todo para la
siguiente bicuadratica, hasta llegar a la última. Luego de la última bicuadratica ACUM contiene el valor
filtrado y el mismo es escrito a los convertidores DAC del CODEC.
5
LISTADO DE PROGRAMAS DE DISEÑO
5.1
FILTROS DE RESPUESTA FINITA (FIR)
/************************************************************************
Programa: FIRF.EXE
Objetivo: Calcular los coeficientes de la funcion de transferencia de un filtro FIR invariante en el tiempo.
El resultado lo almacena en un archivo llamado
FIR.DAT, el mismo tiene un formato capaz de ser
leido por el compilador-C del microprocesador ADSP-21061.
Los coeficientes son calculado utilizando el metodo
de ventana. Para mayor informacion referente a
este metodo ver el Capitulo 10 de:
Introduction to Signal Processing
Sophocles J. Orfanidis
Prentice Hall, 1996
ISBN: 0-13-209172-0
Archivo: FIRF.C
Escrito:
Compilado:
Version:
Version:
Version:
Version:
RICARDO LAMBRANO
Fecha: 06/06/97
MicroSoft C, Ver 6.00
1.00 Escrito
Fecha: 06/06/97
1.10 Documentado
Fecha: 12/12/97
1.20 Coeficientes hasta 2048
Fecha: 03/06/98
2.00 Formato Punto Flotante
Fecha: 23/07/98
************************************************************************/
#include
#include
#include
#include
#define
<stdio.h>
<math.h>
<dos.h>
<graph.h>
PI 3.14159265358
19
/************************************************************************
Funcion: WIN
Objetivo: Calcular el Valor de la funcion de ventana en un punto
Entrada: I,N,VEN
I
= Punto en Ventana
N
= Largo de Ventana
VEN = Tipo de Ventana
Salida: Valor de Ventana (VEN), en punto (I) de (N) puntos
************************************************************************/
double WIN(I,N,VEN)
int
I,N,VEN;
{
double TEMP;
switch(VEN)
{
case
/* Que Tipo de Ventana? */
1:
TEMP = 1.0;
break;
/* Ventana: Rectangular */
/* Calcular Valor */
case
2:
/* Ventana: Hanning */
TEMP = 0.50 + 0.50*cos(2*PI*I/N);
break;
case
3:
/* Ventana: Hamming */
TEMP = 0.540 + 0.460*cos(2*PI*I/N);
break;
case
4:
/* Ventana: Blackman */
TEMP = 0.42 + 0.50*cos(2*PI*I/N) + 0.08*cos(4*PI*I/N);
break;
}
return TEMP;
}
/* Retornar Resultado */
/************************************************************************
Funcion: LP
Objetivo: Calcular coeficientes de Filtro Pasa Bajo
Entrada: FC, FS, N, VEN
FC = Frecuencia de Corte en Hz
FS = Frecuencia de Muestreo en Hz
N
= Numero de Puntos
VEN = Tipo de Ventana
Salida: COEF
COEF = Arreglo con Coeficientes Calculados
************************************************************************/
LP(FC,FS,COEF,N,VEN)
double FC,FS,*COEF;
int
N,VEN;
{
int
I,J,K;
double TEMP,WC;
FC = FC/FS;
WC = 2*PI*FC;
/* Normalizar F-corte */
/* Calcular W-corte */
20
J = -1*N/2;
K = N/2;
/* Limite Inferior */
/* Limite Superior */
for(I = J; I <= K; I++)
/* Ciclo de Calculo */
{
if (I != 0)
/* Si I <> 0 */
{
TEMP = sin(I*WC);
/* Calcular Nominador */
TEMP = TEMP/(PI*I);
/* Dividir por Denominador */
COEF[I+K] = TEMP*WIN(I,N,VEN); /* Ponderar por Ventana */
}
else
/* Cuando I = 0 */
COEF[I+K] = WC*WIN(I,N,VEN)/PI; /* Calcular y Ponderar */
}
}
/************************************************************************
Funcion: HP
Objetivo: Calcular coeficientes de Filtro Pasa Alto
Entrada: FC, FS, N, VEN
FC = Frecuencia de Corte en Hz
FS = Frecuencia de Muestreo en Hz
N
= Numero de Puntos
VEN = Tipo de Ventana
Salida: COEF
COEF = Arreglo con Coeficientes Calculados
************************************************************************/
HP(FC,FS,COEF,N,VEN)
double FC,FS,*COEF;
int
N,VEN;
{
int
I,J,K;
double TEMP,WC;
FC = FC/FS;
WC = 2*PI*FC;
/* Normalizar F-corte */
/* Calcular W-corte */
J = -1*N/2;
K = N/2;
/* Limite Inferior */
/* Limite Superior */
for(I = J; I <= K; I++)
/* Ciclo de Calculo */
{
if (I != 0)
/* Si I <> 0 */
{
TEMP = -sin(I*WC);
/* Calcular Nominador */
TEMP = TEMP/(PI*I);
/* Dividir por Denominador */
COEF[I+K] = TEMP*WIN(I,N,VEN); /* Ponderar por Ventana */
}
else
/* Cuando I = 0 */
/* Calcular y Ponderar */
COEF[I+K] = 1 - WC*WIN(I,N,VEN)/PI;
}
}
/************************************************************************
Funcion: BP
21
Objetivo: Calcular coeficientes de Filtro Pasa Banda
Entrada: FC1, FC2, FS, N, VEN
FC1 = Frecuencia de Corte1 en Hz
FC2 = Frecuencia de Corte2 en Hz
FS = Frecuencia de Muestreo en Hz
N
= Numero de Puntos
VEN = Tipo de Ventana
Salida: COEF
COEF = Arreglo con Coeficientes Calculados
************************************************************************/
BP(FC1,FC2,FS,COEF,N,VEN)
double FC1,FC2,FS,*COEF;
int
N,VEN;
{
int
I,J,K;
double TEMP,WC1,WC2;
FC1
FC2
WC1
WC2
=
=
=
=
FC1/FS;
FC2/FS;
2*PI*FC1;
2*PI*FC2;
J = -1*N/2;
K = N/2;
/*
/*
/*
/*
Normalizar F-corte1 */
Normalizar F-corte2 */
Calcular W-corte1 */
Calcular W-corte2 */
/* Limite Inferior */
/* Limite Superior */
for(I = J; I <= K; I++)
/* Ciclo de Calculo */
if (I != 0)
/* Si I <> 0 */
{
TEMP = sin(I*WC2) - sin(I*WC1); /* Calcular Nominador */
TEMP = TEMP/(PI*I); /* Dividir por Denominador */
COEF[I+K] = TEMP*WIN(I,N,VEN); /* Ponderar por Ventana */
}
else
/* Si I = 0 */
/* Calcular y Ponderar */
COEF[I+K] = (WC2 - WC1)*WIN(I,N,VEN)/PI;
}
/************************************************************************
Funcion: BR
Objetivo: Calcular coeficientes de Filtro Rechaza Banda
Entrada: FC1, FC2, FS, N, VEN
FC1 = Frecuencia de Corte1 en Hz
FC2 = Frecuencia de Corte2 en Hz
FS = Frecuencia de Muestreo en Hz
N
= Numero de Puntos
VEN = Tipo de Ventana
Salida: COEF
COEF = Arreglo con Coeficientes Calculados
************************************************************************/
BR(FC1,FC2,FS,COEF,N,VEN)
double FC1,FC2,FS,*COEF;
int
N,VEN;
{
int
I,J,K;
double TEMP,WC1,WC2;
FC1 = FC1/FS;
FC2 = FC2/FS;
/* Normalizar F-corte1 */
/* Normalizar F-corte2 */
22
WC1 = 2*PI*FC1;
WC2 = 2*PI*FC2;
/* Calcular W-corte1 */
/* Calcular W-corte2 */
J = -1*N/2;
K = N/2;
/* Limite Inferior */
/* Limite Superior */
for(I = J; I <= K; I++)
/* Ciclo de Calculo */
if (I != 0)
/* Si I <> 0 */
{
/* Calcular Nominador */
TEMP = -1.0*(sin(I*WC2) - sin(I*WC1));
TEMP = TEMP/(PI*I); /* Dividir por Denominador */
COEF[I+K] = TEMP*WIN(I,N,VEN); /* Ponderar por Ventana */
}
else
/* Si I = 0 */
/* Calcular y Ponderar */
COEF[I+K] = 1 - 1.0*(WC2 - WC1)*WIN(I,N,VEN)/PI;
}
/************************************************************************
Funcion: DF
Objetivo: Calcular coeficientes de un Diferenciador
Entrada: N, VEN
N
= Numero de Puntos
VEN = Tipo de Ventana
Salida: COEF
COEF = Arreglo con Coeficientes Calculados
************************************************************************/
DF(COEF,N,VEN)
double *COEF;
int
N,VEN;
{
int
I,J,K;
double TEMP;
J = -1*N/2;
K = N/2;
/* Limite Inferior */
/* Limite Superior */
for(I = J; I <= K; I++)
/* Ciclo de Calculo */
if (I != 0)
/* Si I <> 0 */
{
/* Calcular Valor */
TEMP = (cos(PI*I))/I - (sin(PI*K))/(PI*I*I);
COEF[I+K] = TEMP*WIN(I,N,VEN); /* Ponderar por Ventana */
}
else
/* Si I = 0 */
COEF[I+K] = 0;
/* Valor = 0 */
}
/************************************************************************
Funcion: HL
Objetivo: Calcular coeficientes de transformador de Hilbert
Entrada: N, VEN
N
= Numero de Puntos
VEN = Tipo de Ventana
Salida: COEF
COEF = Arreglo con Coeficientes Calculados
************************************************************************/
HL(COEF,N,VEN)
double *COEF;
23
int
N,VEN;
{
int
I,J,K;
double TEMP;
J = -1*N/2;
K = N/2;
/* Limite Inferior */
/* Limite Superior */
for(I = J; I <= K; I++)
/* Ciclo de Calculo */
if (I != 0)
/* Si I <> 0 */
{
TEMP = (1 - cos(PI*I))/(PI*I); /* Calcular Valor */
COEF[I+K] = TEMP*WIN(I,N,VEN); /* Ponderar por Ventana */
}
else
/* Si I = 0 */
COEF[I+K] = 0;
/* Valor = 0 */
}
/************************************************************************
Funcion: MAIN
Objetivo: Cuerpo del Programa
Entrada: Varias desde el Teclado
Salida: Archivo de coeficientes COEF.DAT
************************************************************************/
main()
{
int
TIPO,N,I,MID,VEN;
double FS,FC1,FC2,FACTOR;
static double COEF[2048];
FILE *FILE_OUT;
/************************************************************************
Borrar Pantalla y desplegar menu de tipos de filtro
************************************************************************/
_clearscreen( _GCLEARSCREEN );
printf("\n\n
Programa Para el Diseño de Filtros FIR\n\n");
printf("
Versión 2.00 de 23/07/98 R. Lambraño\n\n");
printf("
1-Pasa Bajos\n");
printf("
2-Pasa Altos\n");
printf("
3-Pasa Banda\n");
printf("
4-Rechaza Banda\n");
printf("
5-Differenciador\n");
printf("
6-Transformada de Hilbert\n\n");
printf("
Seleccione Tipo de Filtro? ");
scanf("%d",&TIPO);
/* Aceptar tipo de Filtro */
printf("
scanf("%d",&N);
Numero de Puntos? ");
/* Aceptar Numero de Coefs */
if (TIPO < 5)
{
printf("Frecuencia de Muestreo (KHz)? ");
scanf("%lf",&FS);
FS = FS * 1000;
/* Si es un filtro */
/* Pedir Frecuencia Muesteo */
/* Aceptar F. muestro KHz */
/* Convertir a Hz */
24
if (TIPO < 3)
/* Si es
{
/* Pedir
printf("
Frecuencia de Corte (Hz)? ");
scanf("%lf",&FC1);
}
else
/* Si es
{
/* Pedir
printf("
Frecuencia de Corte1 (Hz)? ");
scanf("%lf",&FC1);
printf("
Frecuencia de Corte2 (Hz)? ");
scanf("%lf",&FC2);
}
}
LP o HP */
Frec. corte */
LP o HP */
Frec. corte 1 y 2 */
/************************************************************************
Desplegar menu de tipos de Ventana
************************************************************************/
printf("\nVentana
Transicion
Rizo (db)
Relacion
Aten.\n");
printf("1-Rectangular
0.9/N
0.7416
13db
21db\n");
printf("2-Hanning
3.1/N
0.0546
31db
44db\n");
printf("3-Hamming
3.3/N
0.0194
41db
53db\n");
printf("4-Blackman
5.5/N
0.0017
57db
74db\n\n");
printf(" Seleccione Tipo de Ventana? ");
scanf("%d",&VEN);
/* Aceptar Seleccion */
/************************************************************************
Calcular Coeficientes dependiendo de Tipo de Filtro
************************************************************************/
switch(TIPO)
{
case 1:
/* Pasa Bajo */
LP(FC1,FS,COEF,N,VEN);
break;
case 2:
/* Pasa Alto */
HP(FC1,FS,COEF,N,VEN);
break;
case 3:
/* Pasa Banda */
BP(FC1,FC2,FS,COEF,N,VEN);
break;
case 4:
/* Rechaza Banda */
BR(FC1,FC2,FS,COEF,N,VEN);
break;
case 5:
/* Diferenciador */
DF(COEF,N,VEN);
break;
case 6:
/* Hilbert */
HL(COEF,N,VEN);
break;
}
FILE_OUT = fopen("FIR.DAT","w");
/* Abrir Archivo Salida */
FS = FS / 1000.0;
/* Convertir Fs a KHz */
/************************************************************************
Escribir tipo de Filtro en Archivo de Salida
************************************************************************/
switch(TIPO)
25
{
case
case
case
case
case
case
1:
fprintf(FILE_OUT,"//
break;
2:
fprintf(FILE_OUT,"//
break;
3:
fprintf(FILE_OUT,"//
break;
4:
fprintf(FILE_OUT,"//
break;
5:
fprintf(FILE_OUT,"//
break;
6:
fprintf(FILE_OUT,"//
break;
Filtro: LP
\n" );
Filtro: HP
\n" );
Filtro: BP
\n" );
Filtro: BS
\n" );
Filtro: Diferenciador \n" );
Filtro: Hilbert
\n" );
}
/************************************************************************
Escribir tipo de Ventana en Archivo de Salida
************************************************************************/
switch(VEN)
{
case 1:
fprintf(FILE_OUT,"//Ventana: Rectangular
\n");
break;
case 2:
fprintf(FILE_OUT,"//Ventana: Hanning
\n");
break;
case 3:
fprintf(FILE_OUT,"//Ventana: Hamming
\n");
break;
case 4:
fprintf(FILE_OUT,"//Ventana: Blackman
\n");
break;
}
/************************************************************************
Escribir Numero de Coeficientes en Archivo de Salida
************************************************************************/
fprintf(FILE_OUT,"//
Taps: %3d
\n", N );
/************************************************************************
Escribir Frec. Muestreo y Corte en Archivo de Salida
************************************************************************/
if (TIPO < 5)
{
fprintf(FILE_OUT,"//
Fs: %8.3lfKHz
\n", FS );
if (TIPO < 3)
fprintf(FILE_OUT,"//
else
{
fprintf(FILE_OUT,"//
fprintf(FILE_OUT,"//
Fc: %8.3lfHz
\n", FC1 );
Fc1: %8.3lfHz
Fc2: %8.3lfHz
\n", FC1 );
\n", FC2 );
26
}
}
/************************************************************************
Escribir Coeficientes en 1.15 y Flotante en Archivo de Salida
************************************************************************/
for(I = 0; I < (N-1); I++)
{
fprintf(FILE_OUT,"%+19.16lf,
",COEF[I]);
fprintf(FILE_OUT,"// h(%+04d) = %+14.7E \n",I-N/2,COEF[I]);
}
I = N-1;
fprintf(FILE_OUT,"%+19.16lf
",COEF[I]);
fprintf(FILE_OUT,"// h(%+04d) = %+14.7E \n",I-N/2,COEF[I]);
fclose(FILE_OUT);
/* Cerrar Archivo de Salida */
}
/* Fin de Programa */
5.2
FILTROS DE RESPUESTA INFINITA (IIR)
/************************************************************************
Programa: IIRF.EXE
Objetivo: Calcular los coeficientes de la funcion de transferencia de un filtro IIR invariante en el tiempo.
El resultado lo almacena en un archivo llamado
IIR.DAT, el mismo tiene un formato capaz de ser
leido por el ensamblador del microprocesador ADSP-21061.
Los coeficientes son calculado utilizando el metodo
de transformada bilineal aplicada a filtros Butterworth
Para mayor informacion referente a este metodo ver el
Capitulo 11 de:
Introduction to Signal Processing
Sophocles J. Orfanidis
Prentice Hall, 1996
ISBN: 0-13-209172-0
Archivo: IIRF.C
Escrito:
Compilado:
Version:
Version:
Version:
Version:
RICARDO LAMBRANO
MicroSoft C, Ver 6.00
1.00 Escrito
1.10 Documentado
1.20 Incluido Filtro Selectivo
2.00 Formato Punto Flotante
Fecha: 06/06/97
Fecha:
Fecha:
Fecha:
Fecha:
06/06/97
12/12/97
03/06/98
23/07/98
************************************************************************/
#include
#include
#include
#include
#include
<stdlib.h>
<stdio.h>
<dos.h>
<math.h>
<graph.h>
27
#define
PI 3.14159265358
typedef struct complex complex;
/************************************************************************
Funcion: LPHP
Objetivo: Calcular coeficientes de Bicuadraticas de filtros
pasa bajo y pasa alto.
Entrada: FC, FS, N, T
FC = Frecuencia de Corte en Hz
FS = Frecuencia de Muestreo en Hz
N = Numero de Bicuadraticas
T = Tipo de Filtro (0=LP) (1=HP)
Salida: COEF
COEF = Arreglo con coeficientes de bicuadraticas
{SE,B2,B1,B0,A2,A1,SE,B2,...}
SE = Exponente de Normalizacion/Denormalizacion
************************************************************************/
LPHP(FC,FS,COEF,N,T)
double FC,FS,*COEF;
int
N,T;
{
int
I,J,K;
double WC,ANG,G,A1,A2,DEN;
if (T == 0)
WC = tan(PI*FC/FS);
else
WC = 1.0/tan(PI*FC/FS);
/*
/*
/*
/*
Si
Wc
Si
Wc
es LP */
= tan(pi*Fc/Fs */
es HP */
= cot(pi*Fc/Fs */
J = N/2;
/* Correguir Num de Biquad */
for(I = 1; I <= J; I++)
/* Ciclo de Calculo */
{
K = (I - 1)*6;
/* Indice en Arreglo */
ANG = PI*(N - 1 + 2*I)/(2*N);
/* Angulo de Polos */
DEN = (1.0 - 2.0*WC*cos(ANG) + WC*WC); /* Calculo de Denominador */
G = (WC*WC) / DEN;
/* Calculo de G */
A1 = 2.0*(WC*WC-1) / DEN;
/* Calculo de A1 */
A2 = (1.0 + 2.0*WC*cos(ANG)+WC*WC)/ DEN;/* Calculo de A2 */
COEF[K+0] = 0;
COEF[K+1] = G;
if (T == 0)
COEF[K+2] = 2*G;
else
COEF[K+2] = -2*G;
/*
/*
/*
/*
/*
/*
SE
B2
Si
B1
Si
B1
*/
*/
es Pasa Bajo */
*/
es Pasa Alto */
*/
COEF[K+3] = G;
COEF[K+4] = -1*A2;
if (T == 0)
COEF[K+5] = -1*A1;
else
COEF[K+5] = A1;
/*
/*
/*
/*
/*
/*
B0
A2
Si
A1
Si
A1
*/
*/
es Pasa Bajo */
*/
es Pasa Alto */
*/
}
}
/************************************************************************
28
Funcion: BP
Objetivo: Calcular coeficientes de Bicuadraticas de filtro
pasa banda.
Entrada: FC1, FC2, FS, N
FC1 = Frecuencia de Corte1 en Hz
FC2 = Frecuencia de Corte2 en Hz
FS = Frecuencia de Muestreo en Hz
N
= Numero de Bicuadraticas
Salida: COEF
COEF = Arreglo con coeficientes de bicuadraticas
{SE,B2,B1,B0,A2,A1,SE,B2,...}
SE = Exponente de Normalizacion/Denormalizacion
************************************************************************/
BP(FC1,FC2,FS,COEF,N)
double FC1,FC2,FS,*COEF;
int
N;
{
int
I,J,K;
double WC,WC1,WC2,ANG,G,A1,A2,A3,A4,FC,CC,DEN;
double A,B,C,D,Q,R,X;
double M0,M1,M2,N0,N1,N2;
double L,F1,F2;
complex
T,T1,T2,S1,S2;
WC1 = 2*PI*FC1/FS;
WC2 = 2*PI*FC2/FS;
/* Normalizacion de FC1 */
/* Normalizacion de FC2 */
CC = sin(WC1 + WC2)/(sin(WC1) + sin(WC2));
/* Calculo de F central */
WC = fabs( (CC - cos(WC2))/sin(WC2));
/* Calculo de Wc */
J = N/2;
/* Correguir Num de Biquad */
for(I = 1; I <= J; I++)
/* Ciclo de Calculo */
{
K = (I - 1)*12;
/* Indice en Arreglo */
ANG = PI*(N - 1 + 2*I)/(2*N);
/* Angulo de Polos */
DEN = (1.0 - 2.0*WC*cos(ANG) + WC*WC); /* Calculo de Denominador */
G = (WC*WC) / DEN;
/* Calculo de G */
A1 = 4*CC*(WC*cos(ANG) - 1)/DEN;
/* Calculo de A1*Z^-1 */
A2 = 2*(2*CC*CC + 1 - WC*WC)/DEN;
/* Calculo de A2*Z^-2 */
A3 = -4*CC*(WC*cos(ANG) + 1)/DEN;
/* Calculo de A3*Z^-3 */
A4 = (1.0 + 2.0*WC*cos(ANG) + WC*WC)/DEN;/* Calculo de A4*Z^-4 */
/************************************************************************
Los coeficientes antes calculados son de una Ecuacion Cuartica
aqui los convertiremos en dos cuadraticas.
Ref. Pag 2-14 de Handbook of Engineering Fundamentals
Eshbach
Second Edition
Wiley Handbook Series 1952.
ISBN: No existia en esos tiempos
(Las Matematicas nunca cambian...)
a0*X^4 + a1*X^3 + a2*X^2 + a3*X + a4 = 0
dividimos entre a0 para obtener
X^4 + a*X^3 + b*X^2 + c*X + d = 0
29
buscamos cualquier raiz real y1 de
8*Y^3 - 4*B*Y^2 + 2(A*C - 4*D)*Y - (C^2 + D*(A^2 - 4B)) = 0
entonces las dos cuadraticas son
x^2 + (a/2 - (a^2/4 + 2*y1 - b)^.5)*x + (y1 + (y1^2 - d)^.5) = 0
x^2 + (a/2 + (a^2/4 + 2*y1 - b)^.5)*x + (y1 + (y1^2 - d)^.5) = 0
para encontrar una raiz real de una cubica
a*x^3 + b*x^2 + c*x + d = 0
hacemos
q = a*c - b^2
r = 0.5*(3*a*b*c - a^2*d) - b^3
s1 = (r + (q^3 + r^2)^.5)^(1/3)
s2 = (r - (q^3 + r^2)^.5)^(1/3)
Entonces las raices son
x1 = (s1 + s2 - b)/a
x2 = (-.5(s1 + s2) + (-3)^(1/3)*(s1-s2)/2 -b)/a
x3 = (-.5(s1 + s2) - (-3)^(1/3)*(s1-s2)/2 -b)/a
************************************************************************/
/************************************************************************
Calculo de raiz real y1 de cubica
************************************************************************/
A = 8.0;
/* A = 8 */
B = -4*A2/3;
/* B = -4*a2/3 */
C = 2*(A1*A3 - 4*A4)/3;
/* C = 2*(a1*a3 - 4*a4)/3 */
D = -1.0*(A3*A3+ A4*(A1*A1 - 4*A2));
/* D = ... */
Q = A*C - B*B;
R = 0.5*(3*A*B*C - A*A*D) - B*B*B;
/* Calculo de Q */
/* Calculo de R */
L = Q*Q*Q + R*R;
if (L > 0.0)
{
T.x = sqrt(L);
T.y = 0.0;
}
else
{
T.x = 0.0;
T.y = sqrt(fabs(L));
}
/* Argumento de Raiz Cuad Sx*/
/* Si es positiva */
T1.x
T1.y
T2.x
T2.y
S1.x
S1.y
=
=
=
=
=
=
R + T.x;
T.y;
cabs(T1);
atan2(T1.y,T1.x);
pow(T2.x,1.0/3.0);
T2.y / 3.0;
/* Calculo del Resto de S1 */
T1.x
T1.y
T2.x
T2.y
S2.x
=
=
=
=
=
R - T.x;
T.y;
cabs(T1);
atan2(T1.y,T1.x);
pow(T2.x,1.0/3.0);
/* Calculo del Resto de S2 */
/* Resultado es Real */
/* Imaginario = 0 */
/* Si es Negativa */
/* Real = 0 */
/* Resultado Imaginario */
30
S2.y = T2.y / 3.0;
/* Calculo de X1 (REAL */
X = (S1.x*cos(S1.y) + S2.x*cos(S2.y) - B)/A;
/************************************************************************
Calculo de Coeficientes de Cuadraticas
************************************************************************/
F1 = A1*A1/4 + 2*X - A2;
F1 = sqrt(F1);
F2 = X*X - A4;
F2 = sqrt(F2);
N2 = 1;
N1 = A1/2 - F1;
N0 = X + F2;
M2 = 1;
M1 = A1/2 + F1;
M0 = X - F2;
/************************************************************************
Asignacion de Coeficientes de Cuadraticas
************************************************************************/
G = sqrt(G);
/* Ponderacion de Ganancia */
/* En dos bicuadraticas */
/* Este metodo no da buenos */
/* resultados, ver informe */
COEF[K+0]
COEF[K+1]
COEF[K+2]
COEF[K+3]
COEF[K+4]
COEF[K+5]
=
=
=
=
=
=
COEF[K+6]
COEF[K+7]
COEF[K+8]
COEF[K+9]
COEF[K+10]
COEF[K+11]
0;
G;
0;
G;
-1.0*N0;
-1.0*N1;
=
=
=
=
=
=
0;
G;
0;
G;
-1.0*M0;
-1.0*M1;
/*
/*
/*
/*
/*
/*
SE
B2
B1
B0
A2
A1
*/
*/
*/
*/
*/
*/
/*
/*
/*
/*
/*
/*
SE
B2
B1
B0
A2
A1
*/
*/
*/
*/
*/
*/
}
}
/************************************************************************
Funcion: BR
Objetivo: Calcular coeficientes de Bicuadraticas de filtro
Rechaza Banda.
Entrada: FC1, FC2, FS, N
FC1 = Frecuencia de Corte1 en Hz
FC2 = Frecuencia de Corte2 en Hz
FS = Frecuencia de Muestreo en Hz
N
= Numero de Bicuadraticas
Salida: COEF
31
COEF = Arreglo con coeficientes de bicuadraticas
{SE,B2,B1,B0,A2,A1,SE,B2,...}
SE = Exponente de Normalizacion/Denormalizacion
************************************************************************/
BR(FC1,FC2,FS,COEF,N)
double FC1,FC2,FS,*COEF;
int
N;
{
int
I,J,K;
double WC,WC1,WC2,ANG,G,A1,A2,A3,A4,FC,CC,DEN;
double A,B,C,D,Q,R,X;
double M0,M1,M2,N0,N1,N2;
double L,F1,F2;
complex
T,T1,T2,S1,S2;
WC1 = 2*PI*FC1/FS;
WC2 = 2*PI*FC2/FS;
/* Normalizacion de FC1 */
/* Normalizacion de FC2 */
CC = sin(WC1 + WC2)/(sin(WC1) + sin(WC2));
/* Calculo de F central */
WC = fabs( sin(WC2) / (cos(WC2) - CC));
/* Calculo de Wc */
J = N/2;
/* Correguir Num de Biquad */
for(I = 1; I <= J; I++)
/* Ciclo de Calculo */
{
K = (I - 1)*12;
/* Indice en Arreglo */
ANG = PI*(N - 1 + 2*I)/(2*N);
/* Angulo de Polos */
DEN = (1.0 - 2.0*WC*cos(ANG) + WC*WC); /* Calculo de Denominador */
G = (WC*WC) / DEN;
/* Calculo de G */
A1 = 4*CC*WC*(cos(ANG) - WC)/DEN;
/* Calculo de A1*Z^-1 */
A2 = 2*(2*CC*CC*WC*WC + WC*WC - 1)/DEN;/* Calculo de A2*Z^-2 */
A3 = -4*CC*WC*(cos(ANG) + WC)/DEN;
/* Calculo de A3*Z^-3 */
A4 = (1.0 + 2.0*WC*cos(ANG) + WC*WC)/DEN;/* Calculo de A4*Z^-4 */
/************************************************************************
Los coeficientes antes calculados son de una Ecuacion Cuartica
aqui los convertiremos en dos cuadraticas.
Ref. Pag 2-14 de Handbook of Engineering Fundamentals
Eshbach
Second Edition
Wiley Handbook Series 1952.
ISBN: No existia en esos tiempos
(Las Matematicas nunca cambian...)
a0*X^4 + a1*X^3 + a2*X^2 + a3*X + a4 = 0
dividimos entre a0 para obtener
X^4 + a*X^3 + b*X^2 + c*X + d = 0
buscamos cualquier raiz real y1 de
8*Y^3 - 4*B*Y^2 + 2(A*C - 4*D)*Y - (C^2 + D*(A^2 - 4B)) = 0
entonces las dos cuadraticas son
x^2 + (a/2 - (a^2/4 + 2*y1 - b)^.5)*x + (y1 + (y1^2 - d)^.5) = 0
x^2 + (a/2 + (a^2/4 + 2*y1 - b)^.5)*x + (y1 + (y1^2 - d)^.5) = 0
para encontrar una raiz real de una cubica
32
a*x^3 + b*x^2 + c*x + d = 0
hacemos
q = a*c - b^2
r = 0.5*(3*a*b*c - a^2*d) - b^3
s1 = (r + (q^3 + r^2)^.5)^(1/3)
s2 = (r - (q^3 + r^2)^.5)^(1/3)
Entonces las raices son
x1 = (s1 + s2 - b)/a
x2 = (-.5(s1 + s2) + (-3)^(1/3)*(s1-s2)/2 -b)/a
x3 = (-.5(s1 + s2) - (-3)^(1/3)*(s1-s2)/2 -b)/a
************************************************************************/
/************************************************************************
Calculo de raiz real y1 de cubica
************************************************************************/
A
B
C
D
=
=
=
=
8.0;
-4*A2/3;
2*(A1*A3 - 4*A4)/3;
-1.0*(A3*A3+ A4*(A1*A1 - 4*A2));
/*
/*
/*
/*
A
B
C
D
=
=
=
=
8 */
-4*a2/3 */
2*(a1*a3 - 4*a4)/3 */
... */
Q = A*C - B*B;
R = 0.5*(3*A*B*C - A*A*D) - B*B*B;
/* Calculo de Q */
/* Calculo de R */
L = Q*Q*Q + R*R;
if (L > 0.0)
{
T.x = sqrt(L);
T.y = 0.0;
}
else
{
T.x = 0.0;
T.y = sqrt(fabs(L));
}
/* Argumento de Raiz Cuad Sx*/
/* Si es positiva */
/* Resultado es Real */
/* Imaginario = 0 */
/* Si es Negativa */
/* Real = 0 */
/* Resultado Imaginario */
T1.x
T1.y
T2.x
T2.y
S1.x
S1.y
=
=
=
=
=
=
R + T.x;
T.y;
cabs(T1);
atan2(T1.y,T1.x);
pow(T2.x,1.0/3.0);
T2.y / 3.0;
/* Calculo del Resto de S1 */
T1.x
T1.y
T2.x
T2.y
S2.x
S2.y
=
=
=
=
=
=
R - T.x;
T.y;
cabs(T1);
atan2(T1.y,T1.x);
pow(T2.x,1.0/3.0);
T2.y / 3.0;
/* Calculo del Resto de S2 */
/* Calculo de X1 (REAL */
X = (S1.x*cos(S1.y) + S2.x*cos(S2.y) - B)/A;
/************************************************************************
Calculo de Coeficientes de Cuadraticas
************************************************************************/
33
F1 = A1*A1/4 + 2*X - A2;
F1 = sqrt(F1);
F2 = X*X - A4;
F2 = sqrt(F2);
N2 = 1;
N1 = A1/2 - F1;
N0 = X + F2;
M2 = 1;
M1 = A1/2 + F1;
M0 = X - F2;
/************************************************************************
Asignacion de Coeficientes de Cuadraticas
************************************************************************/
G = sqrt(G);
/* Ponderacion de Ganancia */
/* En dos bicuadraticas */
/* Este metodo no da buenos */
/* resultados, ver informe */
COEF[K+0]
COEF[K+1]
COEF[K+2]
COEF[K+3]
COEF[K+4]
COEF[K+5]
=
=
=
=
=
=
COEF[K+6]
COEF[K+7]
COEF[K+8]
COEF[K+9]
COEF[K+10]
COEF[K+11]
0;
G;
-2*G;
G;
-1*N0;
-1*N1;
=
=
=
=
=
=
0;
G;
-2*G;
G;
-1*M0;
-1*M1;
/*
/*
/*
/*
/*
/*
SE
B2
B1
B0
A2
A1
*/
*/
*/
*/
*/
*/
/*
/*
/*
/*
/*
/*
SE
B2
B1
B0
A2
A1
*/
*/
*/
*/
*/
*/
}
}
/************************************************************************
Funcion: NOTCH
Objetivo: Calcular coeficientes de Bicuadraticas de filtros
Notchs.
Entrada: FN, AF, FS, N
FN = Arreglo con Frecuencia Central en Hz
AF = Arreglo con Ancho de Frecuencia en Hz
FS = Frecuencia de Muestreo en Hz
N
= Numero de Bicuadraticas
Salida: COEF
COEF = Arreglo con coeficientes de bicuadraticas
{SE,B2,B1,B0,A2,A1,SE,B2,...}
SE = Exponente de Normalizacion/Denormalizacion
************************************************************************/
NOTCH(FN,AF,FS,COEF,N)
double *FN,*AF,FS,*COEF;
int
N;
{
34
int
I,J,K;
double A1,A2,B,WC;
for(I = 0; I < N; I++)
{
K = I*6;
B = 1.0 / (1.0 + tan(PI*AF[I]/FS));
WC = 2*PI*FN[I]/FS;
A1 = -2.0*B*cos(WC);
A2 = (2*B-1.0);
COEF[K+0]
COEF[K+1]
COEF[K+2]
COEF[K+3]
COEF[K+4]
COEF[K+5]
}
=
=
=
=
=
=
0;
B;
-2*B*cos(WC);
B;
-1*A2;
-1*A1;
/* Ciclo de Calculo */
/*
/*
/*
/*
/*
Indice en Arreglo */
Calculo de Ancho de Banda */
Calculo de Freq. Central */
Calculo de A1 */
Calculo de A2 */
/*
/*
/*
/*
/*
/*
SE
B2
B1
B0
A2
A1
*/
*/
*/
*/
*/
*/
}
/************************************************************************
Funcion: NOTCH
Objetivo: Calcular coeficientes de Bicuadraticas de filtros
Notchs.
Entrada: FN, AF, FS, N
FN = Arreglo con Frecuencia Central en Hz
AF = Arreglo con Ancho de Frecuencia en Hz
FS = Frecuencia de Muestreo en Hz
N
= Numero de Bicuadraticas
Salida: COEF
COEF = Arreglo con coeficientes de bicuadraticas
{SE,B2,B1,B0,A2,A1,SE,B2,...}
SE = Exponente de Normalizacion/Denormalizacion
************************************************************************/
SELEC(FN,AF,FS,COEF,N)
double *FN,*AF,FS,*COEF;
int
N;
{
int
I,J,K;
double A1,A2,B,WC;
for(I = 0; I < N; I++)
{
K = I*6;
B = 1.0 / (1.0 + tan(PI*AF[I]/FS));
WC = 2*PI*FN[I]/FS;
A1 = -2.0*B*cos(WC);
A2 = (2*B-1.0);
COEF[K+0]
COEF[K+1]
COEF[K+2]
COEF[K+3]
COEF[K+4]
COEF[K+5]
}
}
=
=
=
=
=
=
0;
(1-B);
0;
(1-B);
-1*A2;
-1*A1;
/* Ciclo de Calculo */
/*
/*
/*
/*
/*
Indice en Arreglo */
Calculo de Ancho de Banda */
Calculo de Freq. Central */
Calculo de A1 */
Calculo de A2 */
/*
/*
/*
/*
/*
/*
SE
B2
B1
B0
A2
A1
*/
*/
*/
*/
*/
*/
35
/************************************************************************
Funcion: MAIN
Objetivo: Cuerpo del Programa
Entrada: Varias desde el Teclado
Salida: Archivo de coeficientes COEF.DAT
************************************************************************/
main()
{
int
TIPO,N,I,J,MID,INDX;
double FS,FC1,FC2,FACTOR,NORM;
double COEF[512],FN[512],AF[512];
FILE *FILE_OUT;
div_t DIVRES;
/************************************************************************
Borrar Pantalla y desplegar menu de tipos de filtro
************************************************************************/
_clearscreen( _GCLEARSCREEN );
printf("\n\n
Programa Para el Diseño de Filtros IIR\n\n");
printf("
Versión 2.00 de 23/07/98 R. Lambraño\n\n");
printf("
1-Pasa Bajos\n");
printf("
2-Pasa Altos\n");
printf("
3-Pasa Banda\n");
printf("
4-Rechaza Banda\n\n");
printf("
5-Notch\n");
printf("
6-Selectivo\n\n");
printf("
Seleccione Tipo de Filtro? ");
scanf("%d",&TIPO);
/* Aceptar tipo de Filtro */
if(TIPO < 5)
/* Si no es Notch */
{
printf("
Numero de Bi-cuadraticas? ");
scanf("%d",&N);
/* Aceptar Numero de Bicuads */
if (TIPO < 3)
N = N*2;
}
else
/* Si es Notch */
{
printf("
Numero de Filtros? ");
scanf("%d",&N);
/* Numero de Notchs o Selec*/
}
printf("Frecuencia de Muestreo (KHz)? ");
scanf("%lf",&FS);
FS = FS * 1000;
/* Aceptar Frec. Muestreo */
/* Convertir a Hz */
if (TIPO < 3)
/* Si es LP o HP */
{
printf("
Frecuencia de Corte (Hz)? ");
scanf("%lf",&FC1);
/* Aceptar Frec. Corte */
}
if ((TIPO == 3) || (TIPO == 4) )
/* Si es BP o BR */
{
printf("
Frecuencia de Corte1 (Hz)? ");
36
scanf("%lf",&FC1);
/* Aceptar Frec. Corte1 */
printf("
Frecuencia de Corte2 (Hz)? ");
scanf("%lf",&FC2);
/* Aceptar Frec. Corte2 */
}
if (TIPO == 5)
/* Si es Notch */
for(I = 0; I < N; I++)
{
printf("Frecuencia Central del NOTCH%2d (Hz)? ",I);
scanf("%lf",&FN[I]);
/* Aceptar F. Central */
printf("
Ancho de Banda del NOTCH%2d (Hz)? ",I);
scanf("%lf",&AF[I]);
/* Aceptar Ancho de Banda */
}
if (TIPO == 6)
/* Si es Selectivo */
for(I = 0; I < N; I++)
{
printf("Frecuencia Central del Selectivo%2d (Hz)? ",I);
scanf("%lf",&FN[I]);
/* Aceptar F. Central */
printf("
Ancho de Banda del Selectivo%2d (Hz)? ",I);
scanf("%lf",&AF[I]);
/* Aceptar Ancho de Banda */
}
/************************************************************************
Calcular Coeficientes dependiendo de Tipo de Filtro
************************************************************************/
switch(TIPO)
{
case 1:
LPHP(FC1,FS,COEF,N,0);
/* Pasa Bajo */
break;
case 2:
LPHP(FC1,FS,COEF,N,1);
/* Pasa Alto */
break;
case 3:
BP(FC1,FC2,FS,COEF,N);
/* Pasa Banda */
N = N * 2;
break;
case 4:
BR(FC1,FC2,FS,COEF,N);
/* Rechaza Banda */
N = N * 2;
break;
case 5:
NOTCH(FN,AF,FS,COEF,N);
/* Notch */
N = N * 2;
break;
case 6:
SELEC(FN,AF,FS,COEF,N);
/* Selectivo */
N = N * 2;
break;
}
FILE_OUT = fopen("IIR.DAT","w");
/* Abrir archivo de salida */
FS = FS / 1000.0;
/* Convertir Fs a KHz */
/************************************************************************
Escribir tipo de Filtro en Archivo de Salida
37
************************************************************************/
switch(TIPO)
{
case 1:
fprintf(FILE_OUT,"// Filtro: LP
\n" );
break;
case 2:
fprintf(FILE_OUT,"// Filtro: HP
\n" );
break;
case 3:
fprintf(FILE_OUT,"// Filtro: BP
\n" );
break;
case 4:
fprintf(FILE_OUT,"// Filtro: BS
\n" );
break;
case 5:
fprintf(FILE_OUT,"// Filtro: NOTCH
\n" );
break;
case 6:
fprintf(FILE_OUT,"// Filtro: SELECTIVO
\n" );
break;
}
/************************************************************************
Escribir Numero de Polos en Archivo de Salida
************************************************************************/
fprintf(FILE_OUT,"// Polos: %3d
\n", N );
/************************************************************************
Escribir Frecuencia de Muestreo en Archivo de Salida
************************************************************************/
fprintf(FILE_OUT,"//
Fs: %8.3lfKHz
\n", FS );
/************************************************************************
Escribir Frecuencia de Corte en Archivo de Salida
************************************************************************/
if (TIPO < 3)
fprintf(FILE_OUT,"//
Fc: %8.3lfHz
\n", FC1 );
if ((TIPO == 3) || (TIPO == 4) )
{
fprintf(FILE_OUT,"//
Fc1: %8.3lfHz
fprintf(FILE_OUT,"//
Fc2: %8.3lfHz
}
if ( (TIPO == 5) || (TIPO == 6) )
for(I = 0; I < N/2; I++)
{
fprintf(FILE_OUT,"//
fprintf(FILE_OUT,"//
}
\n", FC1 );
\n", FC2 );
Fc: %8.3lfHz
AF: %8.3lfHz
\n", FN[I] );
\n", AF[I] );
/************************************************************************
Escribir Coeficientes en 1.15 y Flotante en Archivo de Salida
************************************************************************/
for(I = 0; I < (N/2); I++)
{
J = I*6;
38
fprintf(FILE_OUT,"%+19.16lf,
fprintf(FILE_OUT,"%+19.16lf,
fprintf(FILE_OUT,"%+19.16lf,
fprintf(FILE_OUT,"%+19.16lf,
fprintf(FILE_OUT,"%+19.16lf,
}
//
//
//
//
//
B2
B1
B0
A2
A1
=
=
=
=
=
%+14.7E\n",COEF[J+1],COEF[J+1]);
%+14.7E\n",COEF[J+2],COEF[J+2]);
%+14.7E\n",COEF[J+3],COEF[J+3]);
%+14.7E\n",COEF[J+4],COEF[J+4]);
%+14.7E\n",COEF[J+5],COEF[J+5]);
fclose(FILE_OUT);
/* Cerrar Archivo de Salida */
}
/* Fin de Programa */
6
LISTADO DE PROGRAMAS DE IMPLEMENTACIÓN
6.1
FILTROS DE RESPUESTA FINITA (FIR)
/***************************************************************************
*
*
Universidad Tecnológica de Panamá
*
Facultad de Ingeniería Eléctrica
*
Departamento de Electrónica
*
Post-Grado en Electrónica Digital
*
Tratamiento Digital de la Informacion
*
*
Fecha:
01/07/98
*
*
Estudiante:
Ricardo Lambraño
*
*
Sistema:
SHARC EZ-KIT Lite
*
Proyecto:
Filtro FIR
*
Archivos:
FIR.C, FIR.DAT
*
*
Archivo:
FIR.C
*
Objetivo:
Archivo Principal
*
Escrito:
05/06/97 Ver.: 1.00 (Analog Devices)
*
Revision:
25/06/98 Ver.: 1.00A Modificado para appl.
*
***************************************************************************/
#include
#include
#include
#include
#include
#include
<def21060.h>
<21060.h>
<signal.h>
<sport.h>
<macros.h>
<math.h>
#define CP_PCI 0x20000
#define CP_MAF 0x1ffff
#define SetIOP(addr, val)
#define GetIOP(addr)
// Definicion de Punteros del DMA
// Direcciones de Memoria Validas
(* (int *) addr) = (val)
(* (int *) addr)
/***************************************************************************
*
*
Definicion de Numero de Coeficientes, Arreglo de Coeficientes
*
y Arreglo de Variables
*
39
***************************************************************************/
#define TAPS 255
// Numero de Coeficientes (Orden del Filtro)
float pm H[TAPS] =
{
#include "fir.dat"
};
// Coeficientes del Filtros leidos del
// Archivo ASCII <FIR.DAT>
float dm X[TAPS+1]; // Buffer del Filtro
/***************************************************************************
*
*
Registros de Inicializacion del CODEC AD1847
*
***************************************************************************/
#define CNT_1847 16
int REGS_1847[CNT_1847] = {
0xc000,
0xc100,
0xc280,
0xc380,
0xc480,
0xc580,
0xc600,
0xc700,
0xc85c,
0xc909,
0xca00,
0xcb00,
0xcc40,
0xcd00,
0xce00,
0x8f00};
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
int rx_buf[3];
int tx_buf[3] = {0xcc40, 0, 0};
// Buffer de Recepcion
// Buffer de Transmision
Registro
Registro
Registro
Registro
Registro
Registro
Registro
Registro
Registro
Registro
Registro
Registro
Registro
Registro
Registro
Registro
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
-
left input control
right input control
left aux 1 input control
right aux 1 input control
left aux 2 input control
right aux 2 input control
left dac control
right dac control
data format
interface configuration
pin control
no register
miscellaneous information
digital mix control
no register
no register
/***************************************************************************
*
*
Bloque de Control de Transferencia (TCB) del DMA
*
***************************************************************************/
typedef struct {
unsigned
unsigned
unsigned
unsigned
unsigned
unsigned**
unsigned
int
unsigned *
} _tcb;
lpath3;
lpath2;
lpath1;
db;
gp;
cp;
c;
im;
ii;
_tcb rx_tcb = {0, 0, 0, 0, 0, 0, 3, 1, 0};
// TCB del Receptor
40
_tcb tx_tcb = {0, 0, 0, 0, 0, 0, 3, 1, 0};
// TCB del Transmisor
static int xmit_count;
static int * xmit_ptr;
// Contador de Datos a transmitir
// Puntero a Datos a transmitir
static int ON_OFF;
// Bandera de filtro encendido o apagado
/***************************************************************************
*
*
Rutina de Programacion del CODEC
*
***************************************************************************/
void SPORT0_TX( int sig_num )
{
if( xmit_count )
// Hay Datos para transmitir
{
tx_buf[0] = *xmit_ptr++;
// Si, Transmitir el actual e inc puntero
xmit_count--;
// Decrementar cuenta de datos a transmitir
}
}
/***************************************************************************
*
*
Rutina de Interrupcion del CODEC (Recepcion)
*
Aqui se realiza el Filtrado!
*
***************************************************************************/
void SPORT0_RX( int sig_num )
{
float ACUM;
int
k,FILT;
ACUM = rx_buf[1];
// Señal de Entra del CODEC (L)
if (ON_OFF)
// Bandera de Funcionamiento
{
X[TAPS] = ACUM;
// X[n] = Ultimo elemento del buffer
ACUM = 0.0;
// Blanquear Acumulador
for(k=0;k<TAPS;k++)
// Ciclo de K = 0 a TAPS
{
ACUM = ACUM + H[k]*X[k];// "Convolucion"
X[k] = X[k+1];
// Corrimiento de Datos
}
}
tx_buf[1] = ACUM;
tx_buf[2] = ACUM;
}
// Señal de Salida del CODEC (L)
// Señal de Salida del CODEC (R)
/***************************************************************************
*
*
Rutina de Interrupcion del IRQ1 (Interruptor)
*
***************************************************************************/
void IRQ1( int sig_num )
{
ON_OFF = ON_OFF ^ 0xFF;
set_flag( SET_FLAG1, TGL_FLAG);
// Cambiar de estado los LEDs
41
set_flag( SET_FLAG2, TGL_FLAG);
set_flag( SET_FLAG3, TGL_FLAG);
}
// LEDs Encendidos indica Filtro Funcionando
// LEDs Apagados indica Fil. no funcionando
/***************************************************************************
*
*
Rutina de inicializacion de los puertos seriales (SPORTS)
*
Los registros aqui inicializados, dependen de la construccion de la
*
tarjeta y no son sujetos a cambios.
*
***************************************************************************/
void INIT_SPORTS( void )
{
sport0_iop.mtcs = 0x00070007;
// Transmitir en canales 0,1,2,16,17,18
sport0_iop.mrcs = 0x00070007;
// Recibir en canales 0,1,2,16,17,18
sport0_iop.mtccs = 0x00000000;
// no companding on transmit
sport0_iop.mrccs = 0x00000000;
// no companding on receive
SetIOP(STCTL0, 0x001c00f2);
SetIOP(SRCTL0, 0x1f8c20f2);
// Inicializacin del Control de Transmision
// Inicializacin del Control de Recepcion
interrupt(SIG_SPR0I, SPORT0_RX);
interrupt(SIG_SPT0I, SPORT0_TX);
interrupt(SIG_IRQ1, IRQ1);
// Establece manejador de Interrupcion RX
// Establece manejador de Interrupcion TX
// Establece manejador de Interrupcion IRQ1
tx_tcb.ii = tx_buf;
// Establece buffer de Transmision
tx_tcb.cp = &tx_tcb.ii;
SetIOP(CP2, (((int)&tx_tcb.ii) & CP_MAF) | CP_PCI);
rx_tcb.ii = rx_buf;
// Establece buffer de Recepcion
rx_tcb.cp = &rx_tcb.ii;
SetIOP(CP0, (((int)&rx_tcb.ii) & CP_MAF) | CP_PCI);
}
/***************************************************************************
*
*
Rutina de Inicializacion del CODEC AD1847
*
***************************************************************************/
void INIT_1847( void )
{
xmit_ptr
= REGS_1847;
// Datos de registros para inicializacion
xmit_count = CNT_1847;
// Cantidad de Datos
while( xmit_count )
idle();
// Esperar hasta tranmitir todos los datos
while( !(rx_buf[0] & 0x0002) )
idle();
// Esperar inicio de Auto-Calibracion
while( rx_buf[0] & 0x0002 )
idle();
// Esperar fin de Auto-Calibracion
}
/***************************************************************************
*
42
*
Rutina de Inicializacion del SHARC 21061
*
***************************************************************************/
void INIT_21K( void )
{
timer_off();
// Des-habilitar el TIMER
xmit_count = 0;
xmit_ptr
= REGS_1847;
// Inicializar variables mientras
// se llegan a utilizar
asm( "#include <def21060.h>" );
asm( "bit set mode1 NESTM;" );
asm( "bit set mode2 IRQ1E;");
// Habilitar interrupciones anidadas
// con instrucciones en Assembler
// Hacer IRQ1 interrupcion por flanco
ON_OFF = 0;
set_flag( SET_FLAG1, SET_FLAG);
set_flag( SET_FLAG2, SET_FLAG);
set_flag( SET_FLAG3, SET_FLAG);
}
//
//
//
//
Bandera ON_OFF apagada
Apagar los LEDs (Indica Filtro Apagado)
Apagar los LEDs (Indica Filtro Apagado)
Apagar los LEDs (Indica Filtro Apagado)
/***************************************************************************
*
*
Cuerpo del Programa
*
***************************************************************************/
void main ( void )
{
int i;
int x;
for( i=0 ; i<TAPS+1 ; i++ )
X[i] = 0.0;
// Inicializar varibles del Filtro
INIT_21K();
// Inicializar el ADSP-21061
set_flag( SET_FLAG0, CLR_FLAG );
FLG0)
for( x=0 ; x<0xffff ; x++ )
;
//
set_flag( SET_FLAG0, SET_FLAG );
// Habilitar el CODEC
INIT_SPORTS();
// Inicializar los puertos seriales
INIT_1847();
// Inicializar el CODEC
while( 3 < 4 )
{
idle();
};
}
// Ciclo inifinito, porque el procesamiento
// se realiza en la rutina de interrupcion
// del CODEC (RX)
6.2
Re-Inicializar
el
CODEC
(conectado
// Esperar el tiempo necesario para el RESET
FILTROS DE RESPUESTA INFINITA (IIR)
/***************************************************************************
*
a
43
*
Universidad Tecnológica de Panamá
*
Facultad de Ingeniería Eléctrica
*
Departamento de Electrónica
*
Post-Grado en Electrónica Digital
*
Tratamiento Digital de la Informacion
*
*
Fecha:
01/07/98
*
*
Estudiante:
Ricardo Lambraño
*
*
Sistema:
SHARC EZ-KIT Lite
*
Proyecto:
Filtro IIR
*
Archivos:
IIR.C, IIR.DAT
*
*
Archivo:
IIR.C
*
Objetivo:
Archivo Principal
*
Escrito:
05/06/97 Ver.: 1.00 (Analog Devices)
*
Revision:
25/06/98 Ver.: 1.00A Modificado para appl.
*
***************************************************************************/
#include
#include
#include
#include
#include
#include
<def21060.h>
<21060.h>
<signal.h>
<sport.h>
<macros.h>
<math.h>
#define CP_PCI 0x20000
#define CP_MAF 0x1ffff
#define SetIOP(addr, val)
#define GetIOP(addr)
// Definicion de Punteros del DMA
// Direcciones de Memoria Validas
(* (int *) addr) = (val)
(* (int *) addr)
/***************************************************************************
*
*
Definicion de Numero de Coeficientes, Arreglo de Coeficientes
*
y Arreglo de Variables
*
***************************************************************************/
#define BICUADS 4
Filtro)
//
Numero
de
Bicuadraticas
(Orden
float pm H[BICUADS*5] =
{
#include "iir.dat"
};
// Coeficientes del Filtros leidos del
// Archivo ASCII <IIR.DAT>
float dm X[BICUADS*5];
// Buffer del Filtro X(n) y Y(n)
/***************************************************************************
*
*
Registros de Inicializacion del CODEC AD1847
*
***************************************************************************/
#define CNT_1847 16
del
44
int REGS_1847[CNT_1847] = {
0xc000,
0xc100,
0xc280,
0xc380,
0xc480,
0xc580,
0xc600,
0xc700,
0xc85c,
0xc909,
0xca00,
0xcb00,
0xcc40,
0xcd00,
0xce00,
0x8f00};
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
int rx_buf[3];
int tx_buf[3] = {0xcc40, 0, 0};
// Buffer de Recepcion
// Buffer de Transmision
Registro
Registro
Registro
Registro
Registro
Registro
Registro
Registro
Registro
Registro
Registro
Registro
Registro
Registro
Registro
Registro
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
-
left input control
right input control
left aux 1 input control
right aux 1 input control
left aux 2 input control
right aux 2 input control
left dac control
right dac control
data format
interface configuration
pin control
no register
miscellaneous information
digital mix control
no register
no register
/***************************************************************************
*
*
Bloque de Control de Transferencia (TCB) del DMA
*
***************************************************************************/
typedef struct {
unsigned
unsigned
unsigned
unsigned
unsigned
unsigned**
unsigned
int
unsigned *
} _tcb;
lpath3;
lpath2;
lpath1;
db;
gp;
cp;
c;
im;
ii;
_tcb rx_tcb = {0, 0, 0, 0, 0, 0, 3, 1, 0};
_tcb tx_tcb = {0, 0, 0, 0, 0, 0, 3, 1, 0};
// TCB del Receptor
// TCB del Transmisor
static int xmit_count;
static int * xmit_ptr;
// Contador de Datos a transmitir
// Puntero a Datos a transmitir
static int ON_OFF;
// Bandera de filtro encendido o apagado
/***************************************************************************
*
*
Rutina de Programacion del CODEC
*
***************************************************************************/
void SPORT0_TX( int sig_num )
{
if( xmit_count )
// Hay Datos para transmitir
{
tx_buf[0] = *xmit_ptr++;
// Si, Transmitir el actual e inc puntero
xmit_count--;
// Decrementar cuenta de datos a transmitir
45
}
}
/***************************************************************************
*
*
Rutina de Interrupcion del CODEC (Recepcion)
*
Aqui se realiza el Filtrado!
*
*
Y(n) = B0*X(n) + B1*X(n-1) + B2*X(n-2) + A1*Y(n-1) + A2*Y(n-2)
*
***************************************************************************/
void SPORT0_RX( int sig_num )
{
float ACUM;
int
i,ii,FILT;
ACUM = rx_buf[1];
// Señal de Entra del CODEC (L)
if (ON_OFF)
// Bandera de Funcionamiento
{
for(ii = 0; ii < BICUADS; ii++)// Repetir por N bicuadraticas
{
i = ii*5;
// Ajustar Indice de variables
X[i+2] = ACUM;
// X[n] = Valor del ADC
// o ultimo filtro!
ACUM = H[i+2]*X[i+2];
// Y[n] = B0*X[n]
ACUM = ACUM + H[i+1]*X[i+1];
// Y[n] = Y[n] + B1*X[n-1]
ACUM = ACUM + H[i+0]*X[i+0];
// Y[n] = Y[n] + B2*X[n-2]
ACUM = ACUM + H[i+3]*X[i+3];
// Y[n] = Y[n] + A1*Y[n-1]
ACUM = ACUM + H[i+4]*X[i+4];
// Y[n] = Y[n] + A2*Y[n-2]
X[i+0] = X[i+ 1];
// X[n-2] = X[n-1]
X[i+1] = X[i+ 2];
// X[n-1] = X[n]
X[i+3] = X[i+ 4];
// Y[n-2] = Y[n-1]
X[i+4] = ACUM;
// Y[n-1] = Y[n]
}
}
tx_buf[1] = ACUM;
tx_buf[2] = ACUM;
}
// Señal de Salida del CODEC (L)
// Señal de Salida del CODEC (R)
/***************************************************************************
*
*
Rutina de Interrupcion del IRQ1 (Interruptor)
*
***************************************************************************/
void IRQ1( int sig_num )
{
ON_OFF = ON_OFF ^ 0xFF;
set_flag( SET_FLAG1, TGL_FLAG);
// Cambiar de estado los LEDs
set_flag( SET_FLAG2, TGL_FLAG);
// LEDs Encendidos indica Filtro Funcionando
set_flag( SET_FLAG3, TGL_FLAG);
// LEDs Apagados indica Fil. no funcionando
}
/***************************************************************************
*
*
Rutina de inicializacion de los puertos seriales (SPORTS)
*
Los registros aqui inicializados, dependen de la construccion de la
46
*
tarjeta y no son sujetos a cambios.
*
***************************************************************************/
void INIT_SPORTS( void )
{
sport0_iop.mtcs = 0x00070007;
// Transmitir en canales 0,1,2,16,17,18
sport0_iop.mrcs = 0x00070007;
// Recibir en canales 0,1,2,16,17,18
sport0_iop.mtccs = 0x00000000;
// no companding on transmit
sport0_iop.mrccs = 0x00000000;
// no companding on receive
SetIOP(STCTL0, 0x001c00f2);
SetIOP(SRCTL0, 0x1f8c20f2);
// Inicializacin del Control de Transmision
// Inicializacin del Control de Recepcion
interrupt(SIG_SPR0I, SPORT0_RX);
interrupt(SIG_SPT0I, SPORT0_TX);
interrupt(SIG_IRQ1, IRQ1);
// Establece manejador de Interrupcion RX
// Establece manejador de Interrupcion TX
// Establece manejador de Interrupcion IRQ1
tx_tcb.ii = tx_buf;
// Establece buffer de Transmision
tx_tcb.cp = &tx_tcb.ii;
SetIOP(CP2, (((int)&tx_tcb.ii) & CP_MAF) | CP_PCI);
rx_tcb.ii = rx_buf;
// Establece buffer de Recepcion
rx_tcb.cp = &rx_tcb.ii;
SetIOP(CP0, (((int)&rx_tcb.ii) & CP_MAF) | CP_PCI);
}
/***************************************************************************
*
*
Rutina de Inicializacion del CODEC AD1847
*
***************************************************************************/
void INIT_1847( void )
{
xmit_ptr
= REGS_1847;
// Datos de registros para inicializacion
xmit_count = CNT_1847;
// Cantidad de Datos
while( xmit_count )
idle();
// Esperar hasta tranmitir todos los datos
while( !(rx_buf[0] & 0x0002) )
idle();
// Esperar inicio de Auto-Calibracion
while( rx_buf[0] & 0x0002 )
idle();
// Esperar fin de Auto-Calibracion
}
/***************************************************************************
*
*
Rutina de Inicializacion del SHARC 21061
*
***************************************************************************/
void INIT_21K( void )
{
timer_off();
// Des-habilitar el TIMER
xmit_count = 0;
// Inicializar variables mientras
47
xmit_ptr
= REGS_1847;
// se llegan a utilizar
asm( "#include <def21060.h>" );
asm( "bit set mode1 NESTM;" );
asm( "bit set mode2 IRQ1E;");
// Habilitar interrupciones anidadas
// con instrucciones en Assembler
// Hacer IRQ1 interrupcion por flanco
ON_OFF = 0;
set_flag( SET_FLAG1, SET_FLAG);
set_flag( SET_FLAG2, SET_FLAG);
set_flag( SET_FLAG3, SET_FLAG);
}
//
//
//
//
Bandera ON_OFF apagada
Apagar los LEDs (Indica Filtro Apagado)
Apagar los LEDs (Indica Filtro Apagado)
Apagar los LEDs (Indica Filtro Apagado)
/***************************************************************************
*
*
Cuerpo del Programa
*
***************************************************************************/
void main ( void )
{
int i;
int x;
for( i=0 ; i<BICUADS*5 ; i++ )
X[i] = 0.0;
// Inicializar varibles del Filtro
INIT_21K();
// Inicializar el ADSP-21061
set_flag( SET_FLAG0, CLR_FLAG );
FLG0)
for( x=0 ; x<0xffff ; x++ )
;
//
set_flag( SET_FLAG0, SET_FLAG );
// Habilitar el CODEC
INIT_SPORTS();
// Inicializar los puertos seriales
INIT_1847();
// Inicializar el CODEC
while( 3 < 4 )
{
idle();
};
}
// Ciclo inifinito, porque el procesamiento
// se realiza en la rutina de interrupcion
// del CODEC (RX)
Re-Inicializar
el
CODEC
(conectado
// Esperar el tiempo necesario para el RESET
a
48
7
RECOMENDACIONES Y MEJORAS
Ambos programas de diseño pueden tener un gran número de mejoras, por ejemplo, para el
programa de diseño de filtros FIR se pueden incluir más ventanas; graficar la respuesta del filtro a
distintas entradas; etc.
Para el programa de diseño de filtros IIR se pueden incluir otros tipos de filtro tales como
Chebyshev Tipo-I, Chebyshev Tipo-II, Elipticos, etc. También como en el caso del programa de IIR se
puede graficar la respuesta del filtro a distintas entradas; mostrar un diagrama de polos y ceros;
calcular el tiempo de estabilización del filtro (setling time); etc.
Para el programa de diseño de filtros IIR se puede y debe mejorar la ponderación de las
ganancias en los filtros Pasa Banda y Rechaza Banda, ya que con filtros de orden mayor a 4 esta
ponderación crea un gran problema, produciendose sobre flujos aun a nivel de audio relativamente bajos.
La ponderación actual de la ganancia es simplemente establecerle una ganancia igual a ambas
secciones bicuadráticas, siendo la ganancia de cada una G1 = G2 = sqrt(G). Obviamente G1*G2 = G.
Sin embargo hemos encontrado que la mejor manera es hacer G1 = sqrt(G)*T y G2 = sqrt(G)/T.
Sin embargo el valor de T adecuado no es fácil de encontrar.
8
BIBLIOGRAFÍA
[1]
Introduction to
Signal Processing
Sophocles J. Orfanidis
Prentice Hall, 1996
[2]
Digital Signal Processing
A Practical Approach
Addison-Wesley, 1993
[3]
Foundations of
Digital Signal Processing
and Data Analysis
James A. Cadzow
Macmillan, 1987
[4]
ADSP-21000 Family
Application Handbook Volume 1
Analog Devices, 1994
[5]
Para listados de programas y más información:
www.ieesa.com/universidades/adsp21061/index.html

Documentos relacionados