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