Processamento Digital de Sinais Utilizando Matlab e Wavelets 9788521621416

Processamento Digital de Sinais – Utilizando MATLAB® e Wavelets é uma excelente ferramenta de estudo sobre este importan

604 64 35MB

Portuguese Pages [435] Year 2012

Report DMCA / Copyright

DOWNLOAD FILE

Polecaj historie

Processamento Digital de Sinais Utilizando Matlab e Wavelets
 9788521621416

Table of contents :
Capa
Frontispício
GEN
Página de rosto
Página de créditos
Dedicatória
Sumário
Prefácio
Material Suplementar
Lista de Figuras
Lista de Tabelas
1 - Introdução
2 - MATLAB®
3 - Filtros
4 - Senoides
5 - Amostragem
6 - A Transformada de Fourier
7 - A Transformada z
8 - A Transformada Discreta de Fourier
9 - A Transformada Contínua de Wavelet
10 - Aplicações
A, B, C, D, E, F
G - Referências
Índice

Citation preview

PROCESSAMENTO DIGITAL DE SINAIS UTILIZANDO MATLAB®

Weeks Zero.indd i

E

WAVELETS

10/07/12 10:40

O GEN | Grupo Editorial Nacional reúne as editoras Guanabara Koogan, Santos, Roca, AC Farmacêutica, Forense, Método, LTC, E.P.U. e Forense Universitária, que publicam nas áreas científica, técnica e profissional. Essas empresas, respeitadas no mercado editorial, construíram catálogos inigualáveis, com obras que têm sido decisivas na formação acadêmica e no aperfeiçoamento de várias gerações de profissionais e de estudantes de Administração, Direito, Enfermagem, Engenharia, Fisioterapia, Medicina, Odontologia, Educação Física e muitas outras ciências, tendo se tornado sinônimo de seriedade e respeito. Nossa missão é prover o melhor conteúdo científico e distribuí-lo de maneira flexível e conveniente, a preços justos, gerando benefícios e servindo a autores, docentes, livreiros, funcionários, colaboradores e acionistas. Nosso comportamento ético incondicional e nossa responsabilidade social e ambiental são reforçados pela natureza educacional de nossa atividade, sem comprometer o crescimento contínuo e a rentabilidade do grupo.

Weeks Zero.indd ii

10/07/12 10:40

SEGUNDA EDIÇÃO

PROCESSAMENTO DIGITAL DE SINAIS UTILIZANDO MATLAB®

E

WAVELETS

Michael Weeks Georgia State University

Tradução e Revisão Técnica Edson Tanaka Engenheiro Eletrônico pela Universidade Federal do Rio de Janeiro, consultor técnico, autor e tradutor de obras de informática e de documentos e publicações técnicas

Weeks Zero.indd iii

10/07/12 10:40

O autor e a editora empenharam-se para citar adequadamente e dar o devido crédito a todos os detentores dos direitos autorais de qualquer material utilizado neste livro, dispondo-se a possíveis acertos caso, inadvertidamente, a identificação de algum deles tenha sido omitida. Não é responsabilidade da editora nem do autor a ocorrência de eventuais perdas ou danos a pessoas ou bens que tenham origem no uso desta publicação. Apesar dos melhores esforços do autor, do tradutor, do editor e dos revisores, é inevitável que surjam erros no texto. Assim, são bem-vindas as comunicações de usuários sobre correções ou sugestões referentes ao conteúdo ou ao nível pedagógico que auxiliem o aprimoramento de edições futuras. Os comentários dos leitores podem ser encaminhados à LTC – Livros Técnicos e Científicos Editora pelo e-mail [email protected]. DIGITAL SIGNAL PROCESSING USING MATLAB® AND WAVELETS, SECOND EDITION ORIGINAL ENGLISH LANGUAGE EDITION PUBLISHED BY Jones & Bartlett Learning, Inc. 40 Tall Pine Drive Sudbury, MA 01776 ISBN: 978-0-7637-84225 COPYRIGHT © 2011 ALL RIGHTS RESERVED.

Direitos exclusivos para a língua portuguesa Copyright © 2012 by LTC __ Livros Técnicos e Científicos Editora Ltda. Uma editora integrante do GEN | Grupo Editorial Nacional Reservados todos os direitos. É proibida a duplicação ou reprodução deste volume, no todo ou em parte, sob quaisquer formas ou por quaisquer meios (eletrônico, mecânico, gravação, fotocópia, distribuição na internet ou outros), sem permissão expressa da editora. Travessa do Ouvidor, 11 Rio de Janeiro, RJ __ CEP 20040-040 Tels.: 21-3543-0770 / 11-5080-0770 Fax: 21-3543-0896 [email protected] www.ltceditora.com.br Capa: Leônidas Leite Editoração Eletrônica: R.O. Moura

CIP-BRASIL. CATALOGAÇÃO-NA-FONTE SINDICATO NACIONAL DOS EDITORES DE LIVROS, RJ W413p Weeks, Michael Processamento digital de sinais utilizando MATLAB® e Wavelets / Michael Weeks ; tradução e revisão técnica Edson Tanaka. – Rio de Janeiro: LTC, 2012. il. ; 28 cm Tradução de: Digital signal processing using MATLAB® and wavelets, 2nd ed. Apêndice Índice ISBN 978-85-216-2141-6 1. MATLAB (Programa de computador). 2. Processamento de sinais - Técnicas digitais Matemática. 3. Processamento de sinais - Técnicas digitais - Simulação por computador. 4. Wavelets (Matemática). I. Título. 12-4273.

Weeks Zero.indd iv

CDD: 621.3822 CDU: 621.391

10/07/12 10:40

Dedico este livro à minha esposa Sophie. Je t’aimerai pour toujours.

Weeks Zero.indd v

10/07/12 10:40

Weeks Zero.indd vi

10/07/12 10:40

Sumário

Prefácio

xiii

1 Introdução 1.1

Números 1 1.1.1 Por que Utilizamos um Sistema Numérico de Base 10? 2 1.1.2 Por que os Computadores Utilizam o Sistema Binário? 2 1.1.3 Por que os Programadores às Vezes Utilizam a Base 16 (Hexadecimal)? 3 1.1.4 Outros Conceitos Numéricos 4

1.2

Conversão entre Representações 4 1.2.1 Conversão de Binário em Decimal 4 1.2.2 Conversão de Decimal em Binário 5 1.2.3 Armazenamento de Ponto Flutuante 6

1.3

Números Complexos 8

1.4

O que É um Sinal? 11

1.5

Analógico versus Digital 14

1.6

O que É um Sistema? 17

1.7

O que É uma Transformada? 18

1.8

A Transformada Aditiva/Subtrativa 19

1.9

Operações com Senoides 20 1.9.1 Traçado de Senoides 21 1.9.2 Amostragem de Sinais Reais 22 1.9.3 Tempo e Frequência 23

1.10

Somas 23

2 MATLAB®

Weeks Zero.indd vii

1

27

2.1

Operações com Variáveis 27

2.2

Como Obter Ajuda e Escrever Comentários 28

2.3

Conceitos Básicos de Programação MATLAB 29 2.3.1 Escalares, Vetores e Matrizes 30 2.3.2 Intervalos de Números 31 2.3.3 Saída 31 2.3.4 Declarações Condicionais (if) 32 2.3.5 Loops 33 2.3.6 Continuação de uma Linha 34

2.4

Exemplos Aritméticos 34

2.5

Funções 43

2.6

Como NÃO Traçar uma Senoide 44

2.7

Traçado de uma Senoide 47

2.8

Traçado de Senoides – um Pouco de Cada Vez 48

10/07/12 10:40

viii

Sumário

2.9

Cálculo de Erro 50

2.10

Às Vezes 0 Não É Exatamente 0 51 2.10.1 Comparação de Números com uma Tolerância 52 2.10.2 Arredondamento e Truncamento 54

2.11

Dicas de Programação em MATLAB 56

2.12

Exercícios de Programação em MATLAB 57

2.13

Outros Comandos MATLAB Úteis 63

3 Filtros 3.1

Componentes de um Filtro 71

3.2

Estruturas do Filtro FIR 73

3.3

Causalidade, Linearidade e Invariância no Tempo 78

3.4

Unidades de Multiplicação e Acumulação 81

3.5

Resposta de Frequência de Filtros 82

3.6

Filtros IIR 87

3.7

Tendências de um Filtro IIR Simples 89

3.8

Correlação 91 3.8.1 Uma Expressão Concisa para um Valor de Correlação Aproximado 92 3.8.2 Covariância Cruzada como um Exemplo Simples de Correlação 93 3.8.3 Exemplos de Correlação 95 3.8.4 Correlação Cruzada 96 3.8.5 Acerca do Deslocamento de um Sinal 99 3.8.6 Um Programa de Correlação Cruzada 102

4 Senoides

Weeks Zero.indd viii

69

111

4.1

Revisão de Geometria e Trigonometria 111

4.2

O Número ␲ 112

4.3

Círculos Unitários 113

4.4

O Principal Valor do Deslocamento de Fase 114

4.5

Amplitudes 115

4.6

Revisão de Números Complexos 116

4.7

Algumas Propriedades Interessantes de j 118 4.7.1 Rotação no Sentido Anti-Horário 118 4.7.2 Rotação no Sentido Horário 118 4.7.3 Remoção de j em 119

4.8

De Onde Vem e? 119

4.9

A Fórmula de Euler 122

4.10

Forma Alternativa da Equação de Euler 123

4.11

A Fórmula Inversa de Euler 123

4.12

Manipulação de Números Complexos 125 4.12.1 Soma de Dois Números Complexos 125 4.12.2 Soma de Números Complexos em Geral 126 4.12.3 Soma de Fasores Girantes 126 4.12.4 Soma de Senoides de Mesma Frequência 127 4.12.5 Multiplicação de Números Complexos 127

4.13

Soma de Fasores Girantes: Um Exemplo 128

4.14

Multiplicação de Fasores 133

10/07/12 10:40

Sumário

4.15

Sinais Harmônicos 133

4.16

Representação de um Sinal Digital como uma Soma de Senoides 136

4.17

Espectro 142

5 Amostragem

149

5.1

Amostragem 149

5.2

Reconstrução 151

5.3

Amostragem e o Ruído de Alta Frequência 151

5.4

Sobreposição Espectral 152 5.4.1 Exemplo de Sobreposição Espectral 153 5.4.2 Dobramento 155 5.4.3 Localização de Replicações após Amostragem 157

5.5

A Taxa de Nyquist 160

5.6

Amostragem de Banda Limitada 161

6 A Transformada de Fourier

169

6.1

Transformada Rápida de Fourier versus Transformada Discreta de Fourier 171

6.2

A Transformada Discreta de Fourier 172

6.3

Traçado do Espectro 174

6.4

Inserção de Amostras Nulas (Zero Padding) 179

6.5

Teoria do Deslocamento para a DFT 180

6.6

A Transformada Discreta de Fourier Inversa 182

6.7

DFT Direta e Inversa 184

6.8

Vazamento Espectral 187

6.9

Harmônicos e a Transformada de Fourier 189

6.10

Amostragem de Frequência e o Espectro 193

6.11

O Funcionamento da DFT 194 6.11.1 Quando a Frequência de Análise Coincide com a Real 195 6.11.2 Quando a Frequência de Análise Não Coincide com a Real 197 6.11.3 Um Detalhe Adicional 199

6.12

Exibição da DFT como Ângulos em Gráficos de Vetores Unitários 201

6.13

Cálculo da DFT Direta e Inversa sob a Forma de Operações Matriciais 203

6.14

Um Programa para o Cálculo da DFT sob a Forma de Operações Matriciais 204

7 A Transformada z

Weeks Zero.indd ix

ix

209

7.1

A Transformada z 209

7.2

Substituição de Dois Filtros FIR em Série 210

7.3

Reexame da Combinação Sequencial de Filtros com z 211

7.4

Por que z−1 É o Mesmo que um Retardo por Uma Unidade? 213

7.5

Como a Transformada z Reduz-se à Transformada de Fourier 214

7.6

Potências de −z 215

7.7

Demonstração da Equivalência da Convolução no Domínio do Tempo em Relação à Multiplicação no Domínio da Frequência 215

7.8

Resposta de Frequência de Filtros 216

7.9

Tendências de um Filtro IIR Simples, Parte II 222

10/07/12 10:40

x

Sumário

8 A Transformada Discreta de Fourier 8.1

O Banco de Filtros de Dois Canais 231

8.2

Filtros Espelhados em Quadratura e Filtros em Quadratura Conjugada 232

8.3

Como a Transformada de Haar Equivale a uma Rotação de 45° 234 8.3.1 Como a Transformada de Haar Afeta o Raio de um Ponto 234 8.3.2 Como a Transformada de Haar Afeta o Ângulo de um Ponto 235

8.4

Wavelet de Quatro Coeficientes de Daubechies 236

8.5

Subamostragem e Sobreamostragem 239 8.5.1 Exemplo Utilizando Sub/Sobreamostradores 239 8.5.2 Subamostragem e Sobreamostragem com Dois Coeficientes 241 8.5.3 Subamostragem e Sobreamostragem com Daubechies 4 242

8.6

Decomposição de um Sinal em Ondas 244

8.7

Projeto de Filtro Wavelet – Filtros com Quatro Coeficientes 253

8.8

Bases Ortonormais 254

8.9

Multirresolução 257

8.10

Wavelets Biortogonais 260

8.11

Teoria da Transformada de Wavelet 264

8.12

Execução da DWT com Matrizes 271 8.12.1 Uma Breve Análise da Multiplicação Matricial 271 8.12.2 Aplicação da DWT 272 8.12.3 Subamostragem com Matrizes 274 8.12.4 Combinação de Passa-Baixas e Passa-Altas em uma Matriz 275 8.12.5 Generalização da DWT com Matrizes 277

8.13

Resposta de Magnitude em Frequência para uma Wavelet 281

9 A Transformada Contínua de Wavelet

287

9.1

A Wavelet Chapéu Mexicano 287

9.2

Deslocamento do Chapéu 289

9.3

Escalonamento do Chapéu 291

9.4

Deslocamento e Escalonamento: Wavelets 292

9.5

Avaliação de Integrais 292

9.6

Multiplicação de uma Função pelo Chapéu Mexicano 296

9.7

Exemplo da CWT 299 9.7.1 Exemplo Analítico da CWT em uma Função 301 9.7.2 Exemplo Analítico da CWT em uma Função Impulso 302 9.7.3 A CWT em uma Função Quadrado 303

9.8

Um Programa para Computar a CWT 304 9.8.1 Convolução 305 9.8.2 Outro Modo de Abordar a Função CWT 305

9.9

Reversão da Transformada Contínua de Wavelet 306

10 Aplicações

Weeks Zero.indd x

227

315

10.1

Exemplos de Aplicação com Som 315

10.2

Exemplos de Aplicação com Imagens 317

10.3

Compressão de uma Foto 319 10.3.1 Uma Abordagem de Baixo para Cima 319 10.3.2 Um Nível Adicional de Simplificação 321

10/07/12 10:40

Sumário

10.3.3 10.3.4

xi

Processamento em Lote 321 Utilização de Imagens Menores em uma Página Web 322

10.4

Execução da Transformada Discreta de Wavelet Bidimensional em uma Imagem 322 10.4.1 DWT Bidimensional de uma Imagem em Tons de Cinza 324 10.4.2 DWT Bidimensional de uma Imagem em Cores 325

10.5

Execução e Reversão da Transformada Discreta de Wavelet 326

10.6

Detecção de Borda 329

10.7

Solução Recursiva de um Quebra-Cabeça Sudoku 332

10.8

Resposta de Magnitude em Frequência de Som 337

10.9

Projeto de Filtro 339 10.9.1 Métodos de Janelamento 339 10.9.2 Projeto de um Filtro FIR 341

10.10 Compressão 345 10.10.1 Experiências com Compressão 345 10.10.2 Compressão de Imagem por Conta Própria 349

A Constantes e Variáveis Utilizadas Neste Livro A.1

Constantes 355

A.2

Variáveis 355

A.3

Símbolos Comuns em Livros sobre DSP 357

B Equações

355

359

B.1

Convolução 359

B.2

Correlação 359

B.3

Fórmula de Euler 360

B.4

Transformada de Fourier 360 B.4.1 Transformada Contínua de Fourier 360 B.4.2 Transformada Discreta de Fourier 360 B.4.3 Transformada Discreta de Fourier Inversa 361 B.4.4 Magnitudes, Fases e Frequências de Análise 361

B.5

Aproximação de Integral 361

B.6

Amostragem 362

B.7

Estatística 362

B.8

Identidades Trigonométricas e Outras Notas Matemáticas 363

B.9

Transformada de Wavelet 364 B.9.1 Transformada Discreta de Wavelet (DWT) 364 B.9.2 Transformada Contínua de Wavelet 365

B.10

Transformada z 365

C Ideias de Projetos de DSP

367

D Conteúdo dos Exemplos Disponíveis no Site da LTC E Respostas para Exercícios Selecionados F Glossário

Weeks Zero.indd xi

371

399

G Referências Índice

369

403

405

10/07/12 10:40

Weeks Zero.indd xii

10/07/12 10:40

Prefácio

O processamento digital de sinais constitui um campo importante e em crescimento, razão pela qual muitos livros têm sido escritos sobre o assunto. Todavia, o fato de nenhum deles atender às necessidades do cientista computacional motivou-me a escrever a presente obra. Assim, ela visa preencher essa lacuna e estabelecer um vínculo com as disciplinas das quais este campo se origina – matemática, engenharia elétrica, física e engenharia mecânica. Esperamos que o leitor encontre aqui explicações lógicas sobre os conceitos de DSP,* juntamente com as ferramentas analíticas nas quais eles se fundamentam.

DSP: Ontem e Hoje Recentemente, perguntaram-me se um estudante que tivesse cursado alguma cadeira de DSP em outra universidade poderia aproveitar os respectivos créditos em nossa universidade. Tratavase de uma pergunta interessante, especialmente porque observei que o referido estudante havia concluído o curso há 10 anos. Quando se trata de ciência da computação, 10 anos é muito tempo. Mas o quanto o DSP havia mudado naquela década? Ocorreu-me que, apesar de algumas mudanças importantes, em termos teóricos as informações básicas não haviam mudado. O programa da disciplina de DSP em questão continha rigorosamente os mesmos tópicos que eu havia coberto em minha aula de DSP naquele verão, apenas com duas exceções: MATLAB® e Wavelets. MATLAB

MATLAB† é uma das diversas ferramentas para o trabalho com matemática. Baseado na minha vasta experiência como programador, de início estava cético. Por que deveria aprender outra linguagem de programação quando podia fazer o trabalho em C/C++? A resposta era simples: é mais fácil trabalhar com o MATLAB! Sim, existem alguns casos em que o uso de outra linguagem seria desejável. Um programa compilado, por exemplo, um escrito em C++, rodará com maior rapidez do que um programa MATLAB interpretado (no qual cada linha é traduzida pelo computador enquanto o programa é executado). Para o design de hardware, pode ser preferível escrever um programa em Verilog ou VHDL, de modo que o programa possa ser convertido em um circuito. Se você já conhece uma linguagem de programação semelhante à C, C++, Java, FORTRAN, etc., você certamente será capaz de abrir um programa MATLAB e entender o que ele faz. Se você estiver começando em programação, encontrará no MATLAB um ambiente amigável onde poderá testar comandos e corrigi-los quando necessário.

* Digital Signal Processing ou Processamento Digital de Sinais. (N.T.) † MATrix LABoratory (N.T.)

Weeks Zero.indd xiii

10/07/12 10:40

xiv

Prefácio

Wavelets

A transformada de wavelet* é uma ferramenta de análise de história relativamente recente. Em 1989, Stéphane Mallat (com alguma ajuda de Meyers) publicou um artigo revolucionário sobre a teoria das wavelets, reunindo técnicas correlatas existentes em diversas disciplinas [1]. De lá para cá, outras pessoas, tais como Ingrid Daubechies [2], Strang e Nguyen [3] e Coifman e Wickerhauser [4] têm agregado significativas contribuições a esta teoria. Escrever sobre esses e outros colaboradores é uma tarefa intimidante, visto que qualquer um que tenha sido eventualmente omitido pode estar lendo isto agora! A transformada de wavelet é uma ferramenta importante com muitas aplicações, entre elas a compressão. Não tenho dúvida de que as futuras gerações de professores de DSP vão classificála como a segunda mais importante, ficando somente atrás da transformada de Fourier. Outros Desenvolvimentos

Outras mudanças recentes na área de DSP incluem os sistemas embutidos, o formato de áudio MP3 e a conscientização pública. As propagandas veiculadas pelos vendedores de telefones celulares que tentam explicar a uma audiência leiga que o processamento digital de sinais é melhor do que o analógico são exemplos da crescente conscientização pública. Na verdade, essas propagandas não tentam informar como o digital é melhor que o analógico, mas efetivamente apontam os problemas inerentes aos telefones analógicos sem fio. Segunda Edição

Esta segunda edição efetua algumas poucas correções e inclui conteúdo adicional em relação à anterior. Como destaque, há um novo capítulo sobre a transformada contínua de wavelet.

Informações do Produto MATLAB Para outras informações sobre o produto MATLAB®, por favor, entre em contato com: The MathWorks, Inc. 3 Apple Hill Drive Natick, MA, 01760-2098 USA Tel.: 508-647-7000 Fax: 508-647-7001 E-mail: [email protected] Web: www.mathworks.com

Agradecimentos Escrever este livro só foi possível graças ao apoio de muitas pessoas. Gostaria de agradecer ao dr. Rammohan Ragade da University of Louisville, que orientou minha pesquisa quando eu ainda a iniciava. Também gostaria de agradecer ao dr. Magdy Bayoumi da University of Louisiana, que me ensinou muito sobre pesquisas e modos de apresentação de resultados para uma

* Ondaleta, ou ondeleta, termos também conhecidos, embora não empregados com tanta frequência quanto wavelet. (N.T.)

Weeks Zero.indd xiv

10/07/12 10:40

Prefácio

xv

audiência. O dr. Marty Fraser, que se aposentou recentemente na Georgia State University, foi meu grande mentor nos primeiros anos na academia. Meus alunos têm sido de valiosa ajuda em todos esses anos apontando eventuais problemas contidos no texto. Gostaria de dirigir um agradecimento especial a Evelyn Brannock, Ferrol Blackmon e Abinashi Dhungel por sua ajuda na revisão do conteúdo. Também gostaria de agradecer aos doutores Kim King e Raj Sunderraman por seu auxílio. Por fim, eu não teria escrito este livro sem o apoio e a compreensão de minha esposa.

– M.C.W. Atlanta, Georgia

Weeks Zero.indd xv

10/07/12 10:40

Material Suplementar Este livro conta com materiais suplementares. O acesso é gratuito, bastando que o leitor se cadastre em http://gen-io.grupogen.com.br.

GEN-IO (GEN | Informação Online) é o repositório de material suplementar e de serviços relacionados com livros publicados pelo GEN | Grupo Editorial Nacional, o maior conglomerado brasileiro de editoras do ramo científico-técnico-profissional, composto por Guanabara Koogan, Santos, Roca, AC Farmacêutica, Forense, Método, LTC, E.P.U. e Forense Universitária.

Weeks Zero.indd xvi

10/07/12 10:40

Lista de Figuras

1.1 1.2 1.3 1.4 1.5 1.6 1.7

Um exemplo de vetor 9 O cálculo de ␪ = arctan(b/a) gera um problema quando a é negativo 10 Um sinal sonoro com uma fita analógica 14 Amostragem de um sinal contínuo 15 Três modos de se visualizar um sinal 17 Um exemplo de sistema 17 Três copos d’água 18

2.1 2.2 2.3 2.4

Uma senoide de 200 Hz produzida pelo código MATLAB do exemplo 46 Utilização da função plotsinusoids 48 Este sinal repete-se a cada segundo 49 Uma visão ampliada de duas senoides de 0.9 a 1 segundo 49

3.1 3.2 3.3 3.4 3.5 3.6 3.7 3.8 3.9 3.10 3.11 3.12 3.13 3.14 3.15 3.16 3.17 3.18 3.19 3.20 3.21 3.22 3.23 3.24 3.25 3.26 3.27 3.28 3.29 3.30 3.31 3.32 3.33 3.34 3.35

Um exemplo de sinal filtrado 69 O conteúdo de frequência do sinal de exemplo e os filtros passa-baixas/altas 70 Um sinal digital, atrasado, aparece como uma versão de si mesmo deslocada no tempo 71 Um somador com dois sinais como entradas 71 Um multiplicador com dois sinais como entradas 72 Um exemplo de filtro FIR com coeficientes 0.5 e −0.5 73 O sinal y é uma versão atrasada de x 73 Filtro FIR com coeficientes {0.5, 0.5} 73 Um exemplo de filtro FIR com coeficientes 0.6 e 0.2 74 Forma geral do filtro FIR 75 Um exemplo de filtro FIR 77 Uma representação de um filtro FIR 77 Condição linear 1: propriedade de escalonamento 79 Condição linear 2: propriedade aditiva 79 Unidade de multiplicação e acumulação 81 Unidades de multiplicação e acumulação como um filtro 82 Resposta de magnitude em frequência para um filtro passa-baixas 83 Resposta de magnitude em frequência para um filtro passa-altas 83 Banda passante, banda de transição e banda de corte, exibidas com ripples 84 Resposta de magnitude em frequência para um filtro passa-banda 85 Resposta de magnitude em frequência para um filtro rejeita-banda 85 Um filtro notch 86 Resposta de magnitude em frequência para um filtro passa-banda com duas bandas passantes 86 Um filtro com realimentação 88 Outro filtro com realimentação 88 Um terceiro filtro com realimentação 88 Forma geral do filtro IIR 89 Um filtro IIR simples 90 Saída de um filtro IIR simples 90 Dois sinais de exemplo, sendo que um parece ser uma versão deslocada do outro 99 Dois sinais de exemplo, alinhados 101 Dois retângulos de tamanho (escala) e rotação diferentes 104 Dois retângulos representados como a distância a partir de seus centros até suas bordas 104 Um retângulo e um triângulo 105 Um retângulo e um triângulo representados como a distância a partir de seus centros até suas bordas 106

Weeks Zero.indd xvii

10/07/12 10:40

xviii

4.1 4.2 4.3 4.4 4.5 4.6 4.7 4.8 4.9 4.10 4.11 4.12 4.13 4.14 4.15 4.16 4.17 4.18 4.19 4.20 4.21 4.22 4.23 4.24 4.25 4.26 4.27 4.28 4.29

Um triângulo retângulo 112 Um exemplo de círculo 112 Um ângulo especificado em radianos 113 Comprimentos de arco para diversos ângulos comuns 114 Uma senoide de 60 Hz 115 Um fasor de −a no ângulo ␪ é igual a a no ângulo (␪ + ␲) 115 Um número complexo pode ser exibido como um ponto ou um fasor 2D 116 Um fasor forma um triângulo retângulo com o eixo x 116 Um fasor girante 117 Um vetor gira − ␲/2 no sentido horário quando multiplicado por −j 119 Juros simples versus compostos 121 Fasores e seus conjugados complexos 124 Gráfico de x(0) em que x(t) = 3e j ␲/6e j2 ␲ 1000t 124 Dois exemplos de números complexos 127 Soma de dois exemplos de números complexos 127 Soma e multiplicação de dois exemplos de números complexos 128 Duas senoides de mesma frequência somadas ponto a ponto e analiticamente 130 Uma representação gráfica da soma de dois fasores de mesma frequência 131 Uma representação gráfica da soma de duas senoides de mesma frequência 132 Um sinal harmônico 135 Um sinal digital (acima) e sua representação de soma de senoides (abaixo) 138 As primeiras quatro senoides no sinal composto 137 As últimas quatro senoides no sinal composto 138 Um sinal curto 141 O sinal curto, repetido 141 Espectro de magnitude em frequência e ângulos de fase 142 Traçado do espectro: magnitude de x(t) = 2 + 2cos(2␲(200)t) 143 Traçado do espectro: magnitudes 144 Traçado do espectro: ângulos de fase 144

5.1 5.2 5.3 5.4 5.5 5.6 5.7 5.8 5.9 5.10 5.11 5.12 5.13

Amostragem de um sinal ruidoso 152 A sobreposição espectral demonstrada 155 cos(−2␲10t − ␲/3) e cos(2␲10t + ␲/3) produzem o mesmo resultado 156 Replicações para f1 = 1 Hz e fs = 4 amostras/segundo 158 Replicações para f1 = 2.5 Hz e fs = 8 amostras/segundo 158 Replicações para f1 = 3 Hz e fs = 4 amostras/segundo 159 Replicações para f1 = 5 Hz e fs = 4 amostras/segundo 159 Replicações para f1 = 3 ou 5 Hz e fs = 4 amostras/segundo 160 Sinal de exemplo amostrado a 2081 Hz 161 Sinal de exemplo amostrado a 100 Hz 161 Sinal de exemplo amostrado a 103 Hz 163 Sinal de exemplo amostrado a 105 Hz 164 Sinal de exemplo amostrado a 110 Hz 164

6.1 6.2

Uma pessoa vocalizando o som “ee” 169 Adagio de Tocata e Fuga em Dó Maior, de J. S. Bach – resposta de magnitude em frequência 170 Uma nota sustentada emitida por uma flauta 170 Comparação de N log2(N) (linha) versus N 2 (asteriscos) 171 Espectro para um sinal de exemplo 177 Espectro aprimorado para um sinal de exemplo 179 Exemplo de saída do programa de deslocamento para a DFT 181 O conteúdo de frequência aparece em frequências de análise exatas 188 O conteúdo de frequência aparece difuso sobre as frequências de análise 188 Aproximação de uma onda triangular com senoides 189 Aproximação de uma onda dente de serra com senoides 191

6.3 6.4 6.5 6.6 6.7 6.8 6.9 6.10 6.11

Weeks Zero.indd xviii

Lista de Figuras

10/07/12 10:40

Lista de Figuras

Weeks Zero.indd xix

6.12 6.13 6.14 6.15 6.16 6.17 6.18 6.19 6.20

Aproximação de uma onda quadrada com senoides 192 Aproximação de uma onda dente de serra + quadrada com senoides 193 Resposta de frequência de um filtro passa-baixa 194 Resposta de frequência de um filtro passa-alta 194 FFT de 2cos(2␲20t) após amostragem simulada 197 FFT de 2cos(2␲21t) após amostragem simulada 198 Resultados da FFT (asteriscos) versus resultados previstos (losangos) 200 Resultados da FFT de exp(j2␲21(n/fs)) (sólida) versus a FFT de exp(−j2␲21(n/fs)) (tracejada) 201 Vetores unitários com ângulos diferentes utilizados na DFT 202

7.1 7.2 7.3 7.4

Filtros FIR em série podem ser combinados 210 Dois filtros FIR comuns 213 Dois filtros FIR comuns, reduzidos 213 Exemplo de traçado de zeros e polos 221

8.1 8.2 8.3 8.4 8.5 8.6 8.7 8.8 8.9 8.10 8.11 8.12 8.13 8.14 8.15 8.16 8.17 8.18 8.19 8.20 8.21 8.22 8.23 8.24 8.25 8.26 8.27 8.28 8.29

Filtros de análise 227 Filtros de síntese 227 O sinal original e sua aproximação e sinal de detalhe DWT 228 Um banco de filtros de dois canais 231 Um filtro espelhado em quadratura para a transformada de Haar 233 Um filtro em quadratura conjugada para a transformada de Haar 233 Um banco de filtros de dois canais com quatro coeficientes 236 Modos diferentes de indicar subamostradores e sobreamostradores 239 Um banco de filtros simples demonstrando sub/sobreamostragem 239 Um banco de filtros simples demonstrando sub/sobreamostragem, reduzido 240 Acompanhamento de sinal da entrada à saída de um banco de filtros simples 240 Um banco de filtros de dois canais com sub/sobreamostradores 241 Um banco de filtros com quatro taps por filtro 242 Análise e reconstrução de wavelet 245 Reconstrução alternativa de wavelet 245 Função impulso analisada com Haar 246 Função impulso analisada com Daubechies-2 247 Função impulso (original) e sua reconstrução 247 Exemplo de função decomposta em três níveis de detalhe e aproximação 248 Função impulso no domínio de Fourier 249 Função impulso no domínio de wavelet 250 Projeto de um banco de filtros com quatro taps cada 253 Dois níveis de resolução 257 Transformada de wavelet biortogonal 261 Transformada de wavelet biortogonal 262 Um canal do lado da análise de um banco de filtros 267 Três oitavas da transformada discreta de wavelet unidimensional 280 O primeiro gráfico do programa show_wavelet 282 O segundo gráfico do programa show_wavelet 282

9.1 9.2 9.3 9.4 9.5 9.6 9.7 9.8 9.9 9.10 9.11

A função Chapéu Mexicano 287 Frequências presentes na função Chapéu Mexicano 288 Visão ampliada das primeiras frequências presentes na função Chapéu Mexicano 289 Função Chapéu Mexicano para um parâmetro t − 3 290 A função Chapéu Mexicano para diferentes parâmetros t 291 A função Triângulo, a função Chapéu Mexicano e a multiplicação das duas 297 A função Triângulo, a função Chapéu Mexicano (centralizada em 4) e a multiplicação das duas 298 A CWT Chapéu Mexicano da função Chapéu Mexicano, centralizada em 5 299 A CWT de uma função constante 300 A CWT de uma função quadrado 303 Uma função e sua reconstrução após a CWT 307

xix

10/07/12 10:40

xx

9.12 9.13 9.14 9.15 9.16 9.17

10.1 10.2 10.3 10.4 10.5 10.6 10.7 10.8 10.9 10.10 10.11 10.12 10.13 10.14

Weeks Zero.indd xx

Lista de Figuras

A CWT da função rampa, quando s = 1/220 308 O Chapéu Mexicano para uma escala muito pequena (s = 1/1024) aparece como um impulso 308 Uma função rampa e sua reconstrução após a CWT, utilizando um pequeno número de escalas 309 Um sinal de exemplo juntamente com a função psi especificada 312 Um sinal de exemplo analisado com a função psi especificada 313 Comparação entre uma linha da transformada de Haar de nosso sinal de exemplo e uma linha da análise com a função psi especificada 314 DWT bidimensional em uma imagem 322 Resultados de DWT_undo para um sinal unidimensional: original e aproximação Resultados de DWT_undo para um sinal unidimensional: detalhes para as oitavas Resultados de DWT_undo para um sinal bidimensional: detalhes para a oitava 1 Resultados de DWT_undo para um sinal bidimensional: detalhes para a oitava 2 Resultados de DWT_undo2 para um sinal bidimensional: detalhes para a oitava 3 Resultados de DWT_undo2 para um sinal bidimensional: detalhes para a oitava 4 Combinação dos detalhes das oitavas 2, 3 e 4 332 Exemplo do programa show_sound 338 Dois filtros similares, com e sem uma transição gradual 340 Utilização de coeficientes de filtro sem janelamento 342 Utilização de coeficientes de filtro com janelamento 343 Coeficientes de filtro com janelamento e aqueles gerados por fir1 343 Inversão alternada para os coeficientes de filtro com janelamento 344

327 1–3 328 330 330 331 331

10/07/12 10:40

Lista de Tabelas

1.1 1.2 1.3 1.4

Quadro de hexadecimal para binário 3 Conversão de decimal em binário 5 Conversão de uma fração decimal em um binário de ponto fixo 6 Sinais de exemplo 12

4.1

Juros compostos sobre US$1000 aproximam-se de US$1000 × e 120

5.1

Frequências detectadas com amostragem de banda limitada 164

6.1 6.2 6.3

Cálculos DFT de exemplo 173 Cálculos DFT de exemplo (forma retangular) 175 Senoides que simplificam as coisas para nós 187

7.1

Convolução de x e h 215

8.1

Função impulso analisada com Haar 247

10.1 10.2

Um quebra-cabeça Sudoku de exemplo 333 A solução do quebra-cabeça Sudoku de exemplo 333

A.1

Alfabeto grego 357

Weeks Zero.indd xxi

10/07/12 10:40

Weeks Zero.indd xxii

10/07/12 10:40

PROCESSAMENTO DIGITAL DE SINAIS UTILIZANDO MATLAB®

Weeks Zero.indd xxiii

E

WAVELETS

10/07/12 10:40

Weeks Zero.indd xxiv

10/07/12 10:40

1

Introdução O Processamento Digital de Sinais (Digital Signal Processing – DSP) constitui um importante campo

de estudo cujo desenvolvimento se deve aos avanços na teoria da comunicação, na tecnologia digital (computacional) e nos dispositivos para consumidores. Existe sempre uma forte necessidade de se melhorarem as coisas, e o DSP oferece muitas técnicas para tal. Por exemplo, as pessoas apreciam música e gostam de descarregá-las (baixá-las). Entretanto, descarregar um arquivo de música pode levar horas caso a velocidade de conexão à Internet seja baixa (tipicamente 56 kilobits por segundo para um modem de linha discada ou dial-up). Com o software de compressão MP3, contudo, o tamanho do arquivo de áudio é reduzido em até 90%, podendo ser descarregado em questão de minutos. A versão MP3 da música não é exatamente igual à original, mas trata-se de uma aproximação “suficientemente boa” a ponto de um ouvinte comum não ser capaz de distinguir uma da outra. Como isso é possível? Em primeiro lugar, é importante saber a respeito da música original (um sinal) e como esta é representada digitalmente. Tal conhecimento leva a um algoritmo de remoção de dados dos quais o ouvinte não sentirá falta. Tudo isso faz parte do Processamento Digital de Sinais. Para entendermos como manipular (processar) um sinal, precisamos antes de tudo saber um pouco mais acerca dos valores que compõem um sinal.

  1.1   Números Considere os nossos conceitos sobre os números. Antigamente, as pessoas usavam números para contar coisas. Na verdade, a letra “A” era originalmente escrita de cabeça para baixo e representava um boi [5] (se escrita de cabeça para baixo, a letra se parece um pouco com a cabeça de um boi). As relações vieram logo em seguida, pois algumas coisas eram mais valiosas do que outras. Se você fosse um criador de porcos daquela época e desejasse um pedaço de pão, estaria disposto a trocá-lo por um porco? Talvez estivesse disposto a trocar um porco por um pedaço de pão e três patos. A questão é que as relações se desenvolveram como uma forma de comparação entre coisas diferentes. Com as relações, vieram as frações. Um pato pode valer três pedaços de pão, e se você negociasse dois patos esperaria receber seis pedaços de pão em troca. Isso também poderia funcionar da maneira inversa. Se você tivesse um pato assado, poderia dividi-lo em três partes iguais e trocar uma delas por um pedaço de pão. As relações conduzem às frações e à utilização do ponto ou vírgula decimal para separar a parte inteira do número da parte fracionária. O zero é um número útil cuja invenção é reivindicada pelos árabes e pelos indianos, embora haja muito debate em torno dessa questão. É possível que suas origens jamais sejam conclusivas. Trata-se de um dos símbolos que utilizamos em nosso sistema de base 10, juntamente com os numerais de 1 a 9. (Imagine se utilizássemos numerais romanos!) O conceito de zero deve ter sido revolucionário em sua época, por tratar-se de uma ideia abstrata. Uma pessoa pode ver um pato ou três pedaços de pão, mas como ver zero alguma coisa? Ainda assim ele é útil, mesmo que apenas um sistema de contagem seja empregado. Também podemos utilizá-lo como um marcador de posição, para diferenciar 10 de 1 ou 100. Dez é um número significativo que utilizamos como a base de nosso sistema numérico decimal. Não há um símbolo para dez, tal como há para 0 a 9 (ao menos, não no sistema numérico decimal). Em vez disso, empregamos uma combinação de símbolos, ou seja, 10. Entendemos que o 1 é para o dígito das dezenas e que o 0 é o dígito das unidades, de modo que localização é um aspecto importante. Em qualquer sistema numérico temos essa ideia de localização, na qual os dígitos à esquerda são maiores em magnitude do que os dígitos à direita. Isso também é verdade nos sistemas numéricos que os computadores utilizam, ou seja, binários. 1

Weeks 001.indd 1

14/08/11 19:40

2    CAPÍTULO 1

  1.1.1   Por que Utilizamos um Sistema Numérico de Base 10? A hipótese mais provável é a de que utilizamos um sistema numérico de base 10 porque os seres humanos possuem 10 dedos e os utilizavam para contar, tal como as crianças ainda fazem hoje. Para nós, é um sistema conveniente.

  1.1.2   Por que os Computadores Utilizam o Sistema Binário? Os computadores utilizam internamente a base 2, binária. Esse sistema numérico funciona bem com os componentes internos do computador – a saber, os transistores. Um transistor funciona como uma chave pela qual a eletricidade flui facilmente quando uma determinada carga (por exemplo, 5 volts) é aplicada à porta, e o fluxo de eletricidade é significativamente bloqueado quando outra carga, oposta (por exemplo, 0 volt), é aplicada. Existem tipos diferentes de transistores, mas examinaremos aqui o Semicondutor Complementar de Óxido Metálico (Complementary Metal Oxide Semiconductors – CMOS). É possível que você também encontre outra definição para esse termo, com “Silício” no lugar de “Semicondutor”. Silício é um tipo de semicondutor que permite o fluxo (ou a condução) de eletricidade durante parte do tempo, dependendo das condições. Dois tipos de material são empregados na fabricação desses transistores: silício misturado (dopado) com um material carregado negativamente e silício misturado com um material carregado positivamente, formando silício tipo n e silício tipo p, respectivamente. Se esses tipos de material forem dispostos um ao lado do outro no padrão n-p-n, um transistor tipo n será criado. Com o padrão p-n-p, o transistor criado será do tipo p. A porta corresponde ao material que fica no meio e controla o comportamento do transistor. O transistor tipo n permite o fluxo de corrente quando uma determinada carga positiva (por exemplo, 5 volts) é aplicada na porta. Ele bloqueia o fluxo de corrente quando 0 volt (a terra) é aplicado na porta. O transistor tipo p funciona de maneira inversa: um valor terra na porta permite o fluxo de eletricidade, enquanto a carga positiva bloqueia o fluxo. O nome CMOS advém do fato de cada transistor tipo n ser utilizado com um transistor tipo p correspondente, numa forma complementar. Se encararmos os dois valores que trabalham com esses transistores como um 0 lógico (terra) e um 1 lógico (por exemplo, 5 volts, dependendo do sistema), teremos um sistema numérico de dois valores. Um valor ou está em “on” (ativo) ou em “off” (inativo). Uma das vantagens da utilização de um sistema binário está na possibilidade de correção. Por exemplo, se um valor binário aparecer como 0,3 volt, uma pessoa poderá concluir que esse valor deve ser 0,0 volt, ou 0 lógico. No interior de um computador, tal valor aplicado a um circuito lógico digital estaria suficientemente próximo de 0,0 volt para funcionar da mesma maneira, mas seria transmitido pelo circuito como um valor corrigido. Ao utilizarmos um par de transistores complementares, criamos um inversor – isto é, alimentamos dois transistores com a mesma entrada: um transistor (tipo p) conecta a saída a um 1 lógico, enquanto o outro transistor (tipo n) conecta a saída ao 0 lógico.* A saída será conectada ao 1 lógico ou ao 0 lógico, mas corresponderá ao valor oposto da entrada. De uma forma semelhante, podemos construir portas NAND e portas NOR utilizando mais transistores. Com qualquer uma dessas portas, podemos criar qualquer função binária arbitrária que desejarmos. Os circuitos integrados (chamados informalmente de chips computacionais), feitos de silício e com milhões de transistores, podem ser fabricados em grande quantidade por um custo baixo. Esse campo é conhecido como Integração em Escala Muito Grande (Very Large Scale Integration – VLSI). Se você teve dificuldades em entender a explicação anterior, não se preocupe. Basta dizer que existem razões subjacentes, tanto físicas quanto econômicas, que levaram ao uso de um sistema numérico binário em computadores. Com os dois valores do sistema binário, podemos criar qualquer função lógica que desejarmos, independentemente do número de entradas.

* Quando se aplica a mesma entrada a ambas as portas, um dos transistores está conduzindo, enquanto o outro está aberto (bloqueando a corrente). (N.T.)

Weeks 001.indd 2

14/08/11 19:40

Introdução    3

  1.1.3    Por que os Programadores às Vezes Utilizam a Base 16 (Hexadecimal)? Os computadores utilizam o sistema binário, que é ótimo para um computador, mas difícil para uma pessoa. Por exemplo, o que é mais fácil de lembrar: 0011001000010101 ou 3215? Na prática, esses dois números representam a mesma quantidade – o primeiro está em binário e o segundo está em hexadecimal. Como você pode ver, a representação hexadecimal proporciona uma forma mais fácil para as pessoas trabalharem com longas sequências de zeros e uns, e sua conversão para binário (e vice-versa) é bem simples. Na verdade, um dígito hexadecimal representa quatro bits. Com um grupo de quatro bits, as únicas possibilidades são: 0000, 0001, 0010, 0011, 0100, 0101, 0110, 0111, 1000, 1001, 1010, 1011, 1100, 1101, 1110 e 1111. As dez primeiras dessas 16 combinações de quatro bits possíveis correspondem aos números 0, 1, 2, 3, 4, 5, 6, 7, 8 e 9. Neste ponto nos deparamos com um problema, pois seríamos tentados a empregar “10” como o próximo número, da mesma forma que faríamos em decimal. O problema desse procedimento reside no fato de ele ser ambíguo: “210” significaria 2, 1, 0 ou 2, 10? Visto que temos 16 valores diferentes em um grupo de quatro dígitos binários, precisamos de 16 símbolos diferentes, um para cada valor. Essa é a razão pela qual os caracteres do alfabeto são empregados em seguida: A, B, C, D, E e F. A Tabela 1.1 exibe um quadro com dígitos hexadecimais e suas correspondentes representações em decimal e em binário. Uma observação: você pode encontrar valores hexadecimais tanto em maiúsculas quanto em minúsculas. Por exemplo, 1a3f é o mesmo que 1A3F. A utilização de registros de 1 bit é ineficiente, razão pela qual os computadores agrupam bits em uma palavra. O tamanho de palavra depende da arquitetura (por exemplo, o “64” no Nintendo® 64 representa o tamanho de palavra de 64 bits de seu processador). Tipicamente, o tamanho de palavra é um múltiplo de um byte (8 bits), e números hexadecimais funcionam bem com tais máquinas. Por exemplo, uma máquina com um tamanho de palavra de 1 byte trabalharia com tamanhos de dados de dois dígitos hexadecimais de uma vez. Um tamanho de palavra de 16 bits implica a utilização de quatro dígitos hexadecimais simultaneamente. Eis um exemplo que demonstra o tamanho de palavra. Suponha que efetuemos duas multiplicações, da seguinte forma: 9 × 3      4632 × 9187 Podemos efetuar as duas multiplicações, porém a maioria das pessoas possui uma resposta imediata para a da esquerda, mas precisa de um minuto ou dois para a da direita. Por quê? Embora possam ter decorado as respostas para a multiplicação com um único dígito, elas precisam utilizar um algoritmo para

Tabela 1.1

Quadro de hexadecimal para binário.

Dígito hexadecimal 0 1 2 3 4 5 6 7 8 9 a b c d e f

Weeks 001.indd 3

Binário 0000 0001 0010 0011 0100 0101 0110 0111 1000 1001 1010 1011 1100 1101 1110 1111

Equivalente decimal 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

14/08/11 19:40

4    CAPÍTULO 1 as multiplicações com múltiplos dígitos, ou seja, multiplicar os dígitos mais à direita, escrever o resultado, escrever o dígito de transporte, mover um dígito para a esquerda para o número do “topo” e repetir o processo. De certo modo, temos um tamanho de palavra de 1 dígito quando efetuamos esse cálculo. Da mesma forma, um computador multiplicará (ou adicionará, subtrairá etc.) nos termos de seu tamanho de palavra. Suponha, por exemplo, que um computador tenha um tamanho de palavra de 8 bits. Se ele precisasse incrementar um número de 16 bits, adicionaria 1 aos 8 bits baixos e, em seguida, adicionaria o transporte aos 8 bits altos. A título de exercício, utilize a Tabela 1.1 para verificar se a sequência binária 0011001000010101 corresponde a 3215 em hexadecimal. A observação-chave a ser feita é que devemos distribuir os dígitos binários em grupos de quatro, começando pelo bit mais à direita e progredindo para a esquerda. Suponha, por exemplo, que em vez da primeira tenhamos recebido a sequência binária 011001000010101. Se agruparmos a partir da direita, obteremos 011 0010 0001 0101. O grupo mais à esquerda possui apenas três dígitos, mas podemos completá-lo acrescentando um zero à esquerda para criar o grupo 0011. Na realidade, podemos acrescentar quantos zeros desejarmos, tal como fazemos em decimal. Ou seja, os números decimais 3, 03 e 003 possuem todos o mesmo valor. A complementação com zeros à esquerda não altera o valor em binário, assim como não altera o valor em decimal. Quer comecemos com 0011001000010101, 011001000010101 ou até mesmo 11001000010101, deveremos chegar à mesma representação hexadecimal.

  1.1.4   Outros Conceitos Numéricos Números negativos constituem outro conceito útil que surge devido ao empréstimo. Não é difícil imaginar que alguém está lhe devendo certa quantidade de algum item. Como você representaria isso? O que utilizamos hoje em dia é uma quantidade negativa, ou seja, uma conta-corrente no vermelho, para indicar não apenas que se possui zero de alguma coisa, mas também que se deve uma quantidade a outra pessoa. Infinito é outra ideia estranha, porém útil. Com o nosso sistema numérico, não importa que número extremamente grande alguém possa inventar, você ainda poderá adicionar 1 a ele. Assim, utilizamos o infinito para representar o maior número possível. Com a inclusão do infinito negativo e do infinito como os limites de nossa escala, podemos abordar todos os números. Isso é visto frequentemente em fórmulas como uma forma de abranger todos os números.

  1.2   Conversão entre Representações A maior parte do que discutiremos estará centrada em números inteiros e de ponto fixo. Ponto fixo significa exatamente o que o nome indica – o ponto fracionário é fixo. Ponto fracionário* é o nome atribuído ao símbolo que separa a parte inteira da parte fracionária – um ponto nos Estados Unidos e uma vírgula na Europa.** Não se trata exatamente de um ponto decimal, pois este termo implica a base 10 (decimal) estar sendo utilizada. Um número de ponto fixo pode ser convertido em decimal (e vice-versa) da mesma forma pela qual um número binário é convertido.

  1.2.1   Conversão de Binário em Decimal Para converter um número binário em decimal, multiplica-se sequencialmente cada bit por um múltiplo de 2, da direita para a esquerda. Portanto, para o número binário 01101001, o equivalente decimal é 0 × 27 + 1 × 26 + 1 × 25 + 0 × 24 + 1 × 23 + 0 × 22 + 0 × 21 + 1 × 20 = 1 × 64 + 1 × 32 + 1 × 8 + 1 × 1 = 105.

* Radix point, também conhecido como vírgula aritmética ou vírgula decimal. (N.T.) ** Embora a vírgula seja adotada como ponto decimal também no Brasil, para a tradução desta obra foi adotado o padrão americano, devido ao fato de o Matlab trabalhar neste padrão. Observe que mantivemos a ausência do zero para números fracionários, ex.: .375 (= 0, 375) devido ao programa. (N.T.)

Weeks 001.indd 4

14/08/11 19:40

Introdução    5

Para um número de ponto fixo essa ideia é válida, exceto pelo fato de o bit à esquerda do ponto fracionário corresponder a 20. É possível começar aqui e prosseguir para a esquerda, depois começar novamente no ponto fracionário e prosseguir para a direita. Por exemplo, a conversão do número de ponto fixo 01010.111 seria 0 × 24 + 1 × 23 + 0 × 22 + 1 × 21 + 0 × 20 + 1 × 2−1 + 1 × 2−2 + 1 × 2−3 = 1 × 8 + 1 × 2 + 1 × (1/2) + 1 × (1/4) + 1 × (1/8) = 10.875 em decimal. Números negativos requerem uma etapa adicional. Normalmente, um computador armazena números negativos na forma de complemento de dois. O bit mais à esquerda determina se o número é positivo (0) ou negativo (1). A conversão de um número negativo em positivo, ou vice-versa, corresponde à execução da operação de complemento de um em todos os bits, seguida da adição de um. O complemento de um significa simplesmente a inversão de cada bit. Suponha, por exemplo, que o número 11010010 esteja armazenado na forma de complemento de dois. Para encontrar seu valor positivo correspondente, primeiro invertemos todos os bits para obter 00101101. Em seguida, adicionamos um ao resultado, obtendo 00101110. Tenha em mente que 1 + 1 = 10 em binário. Na conversão em decimal, verificamos que o resultado é 1 × 25 + 1 × 23 + 1 × 22 + 1 × 21 = 32 + 8 + 4 + 2 = 46. Portanto, concluímos que 11010011 corresponde ao número –46 na forma de complemento de 2.

  1.2.2   Conversão de Decimal em Binário Para encontrarmos o correspondente de um número decimal em binário, separamos a parte inteira da parte fracionária. Tomamos a parte inteira, dividimos por 2 e acompanhamos o resultado e seu resto. Em seguida, repetimos esse procedimento com o resultado até que o resultado subsequente seja zero. Ao lermos os restos de volta, de baixo para cima, obtemos nosso número binário. Suponha, por exemplo, que desejemos converter o número decimal 4 em binário: 4/2 = 2 resto 0 2/2 = 1 resto 0 1/2 = 0 resto 1. Desse modo, 4 em decimal equivale a 100 em binário. Podemos anteceder nossa resposta com 0 para evitar confusão com um número binário negativo, obtendo assim 0100 em binário. No caso de um número decimal fracionário, multiplique por 2 e mantenha a parte inteira. Em seguida, repita a operação com a parte fracionária restante até que ela seja zero (embora seja possível que ela se repita indefinidamente, tal como ocorre com 1/3 em decimal). Ao concluir, leia as partes inteiras de volta, de cima para baixo. Por exemplo, digamos que desejemos converter .375 em binário: .375 × 2 = 0 mais .75 .75 × 2 = 1 mais .5 .5 × 2 = 1 mais 0. Nossa resposta para a operação é .011 em binário. Podemos juntar as respostas e concluir que 4.375 em decimal equivale a 0100.011 em binário. Como outro exemplo, suponha que desejemos converter o número 38 em binário. Como podemos observar na Tabela 1.2, os restos (em ordem reversa) são 100110. Uma vez que não estabelecemos o número

Tabela 1.2

Conversão de decimal em binário.

Número 38 19 9 4 2 1

Weeks 001.indd 5

Número/2 19 9 4 2 1 0

Resto 0 1 1 0 0 1

14/08/11 19:40

6    CAPÍTULO 1

Tabela 1.3

Conversão de uma fração decimal em um binário de ponto fixo. Número fracionário 0,1 0,2 0,4 0,8 0,6 0,2

Número × 2 Parte inteira 0,2 0 0,4 0 0,8 0 1,6 1 1,2 1 0,4 0

de bits a serem utilizados no armazenamento do número binário de ponto fixo, e devido ao fato de o bit mais à esquerda geralmente indicar o sinal, acrescentamos um zero à esquerda para evitar confusão. Assim, nossa resposta passa a ser 0100110. Podemos verificá-la convertendo-a de volta à forma decimal: 32 + 4 + 2 = 38. Esse método pressupõe que o número decimal é positivo. Quando ele for negativo, utilize o valor absoluto em seu lugar. Ao final, encontraremos o complemento de dois: converta todos os zeros em uns e todos os uns em zeros, adicionando um em seguida. Essa etapa final fornecerá o padrão de bits correto para números negativos. Não nos preocuparemos com os números negativos abaixo, pois o padrão de ponto flutuante utiliza um bit de sinal em vez de empregar o complemento de dois. Dada uma sequência de números binários, como podemos saber se ela está armazenada na forma de complemento de dois? Não temos como saber. Precisamos conhecer o contexto. E se precisarmos converter 38.1 em uma representação binária de ponto fixo? Já conhecemos os bits à esquerda do ponto fracionário e só precisamos utilizar um algoritmo semelhante para encontrar a parte fracionária. Começamos com o número fracionário, que multiplicamos por 2 (ou, caso você prefira pensar desta maneira, dividimos por 2−1), e acompanhamos a parte inteira. Repetimos esse procedimento com a parte fracionária do número anterior até obtermos um zero para a parte fracionária. A Tabela 1.3 exibe os resultados para este exemplo. Observe que na Tabela 1.3 o último número fracionário, 0.2, já está presente na tabela. Essa parte fracionária não possui uma representação binária compacta. Em vez disso, tal como o número 1/3 em decimal, ela se repete indefinidamente. O melhor que podemos fazer é armazenar uma aproximação de 00011. Ao utilizarmos essas duas técnicas decimal-em-binário em conjunto, concluímos que 38.1 convertido em binário de ponto fixo equivale a: 0100110.00011. E se desejarmos armazenar esse valor como um número de ponto flutuante? É o que discutiremos a seguir.

  1.2.3   Armazenamento de Ponto Flutuante O Institute of Electrical and Electronics Engineers – IEEE possui um padrão para o armazenamento de ponto flutuante em 32 ou 64 bits, geralmente chamado de padrão IEEE 754 [6]. Decompomos a representação binária em três campos: o bit de sinal, o significando e o expoente. (O significando também pode ser chamado pelo antigo nome de mantissa.) Em outras palavras, o número aparece como ± significando × 2±expoente. Naturalmente, o tamanho da palavra de armazenamento afeta diretamente a precisão. No padrão IEEE 754, chamamos de precisão o número de bits empregados para representar o significando. Tal como mostra o exemplo do número 38.1, o significando que armazenamos depende da precisão. Com 32 bits para a representação de ponto flutuante completa (chamada de precisão simples), utilizamos 23 bits para o significando. Quando temos uma palavra de 64 bits (precisão dupla), utilizamos 52 bits para ela. Um bit armazena o sinal, e os bits restantes armazenam o expoente. Desse modo, o expoente consistirá em 8 ou 11 bits, de acordo com a precisão.

Weeks 001.indd 6

14/08/11 19:40

Introdução    7

Você também poderá encontrar casos de precisão de 24 e 53 bits, respectivamente. O bit a mais é explicado pelo bit “oculto”, que não é armazenado explicitamente [6]. Lembre-se de que o significando consiste somente em zeros e uns e que a magnitude de cada bit depende do expoente. Por exemplo, 0.1 × 20 possui o mesmo valor que 1.0 × 2−1. Se podemos estabelecer sempre o primeiro bit como 1 bastando alterar o expoente, por que não impor tal condição? Assim, não precisamos armazenar esse bit. O padrão IEEE 754 emprega essa estratégia. Uma observação final sobre o padrão IEEE 754 é que o expoente varia de −126 a +127 (para um expoente de 8 bits) ou de −1022 a +1023 (para expoentes de 11 bits). O computador armazena esses expoentes com um desvio(bias) de 127 e 1023, respectivamente. Para encontrarmos o valor real do expoente a ser utilizado, subtraímos esse valor de desvio do valor armazenado. Suponha, por exemplo, que o valor do expoente armazenado para um valor em ponto flutuante de 32 bits seja 10000011 em binário, ou 131 em decimal. O valor real do expoente seria 131 − 127 = 4, de modo que o número no significando é multiplicado por 24. Com 126 números de expoente negativo possíveis, 127 números positivos para o expoente e 1 número para o expoente zero, temos 254 valores possíveis para o expoente. As outras duas possibilidades levam em conta significados especiais, tal como um significando zero quando todos os bits do expoente são 0. Caso contrário, os bits armazenados no significando seriam interpretados como se começassem com um 1 oculto. Vamos criar um valor duplo (double) e exibi-lo na tela em hexadecimal. Isso pode ser feito facilmente em MATLAB®. Mesmo que você não esteja familiarizado com o MATLAB, experimente os comandos a seguir. Os dois sinais maior que (>>) representam o prompt de comando, de modo que você não precisa digitá-los. As linhas abaixo de cada comando exibem a resposta do computador. A sequência de formatação no comando sprintf pode ser um pouco intimidante. O sinal de porcentagem indica que uma variável deve ser impressa, tal como na linguagem de programação C. O x e o \n são padrão na formatação estilo C, representando hexadecimal e nova linha, respectivamente. Poderíamos dispensar o \n, mas ele nos proporciona um espaço conveniente antes do prompt MATLAB seguinte. O parâmetro b de formatação final instrui o MATLAB a utilizar um tipo double. Deste modo, visualizamos os valores double como sequências hexadecimais que podem ser facilmente convertidas em binário com um quadro de consulta. >> disp(sprintf(%bx \n,0.0)) 0000000000000000 >> disp(sprintf(%bx \n,-0.0)) 8000000000000000 >> disp(sprintf(%bx,38.1)) 40430ccccccccccd Ao examinarmos a saída MATLAB anterior, percebemos que o zero e o zero negativo são efetivamente armazenados de duas maneiras diferentes! O MATLAB se encarrega disso para nós, conforme mostrado no código a seguir: if (-0.0 == 0.0) disp(equal); else disp(not equal); end Ao executarmos o código, constatamos que ele se comporta da forma esperada. equal Em outras palavras, embora o zero e o zero negativo possuam representações internas diferentes, o MATLAB trata-os como iguais. Vamos citar mais um exemplo de um número armazenado no padrão IEEE 754. Observe o parâmetro t, que instrui o MATLAB a tratar o número como um valor de 32 bits em ponto flutuante.

Weeks 001.indd 7

14/08/11 19:40

8    CAPÍTULO 1 >> disp(sprintf(%tx,16.875)) 41870000 Podemos utilizar o quadro na Tabela 1.1. Ao escrevermos os valores binários, temos: hexa: binário:

4 0100

1 0001

8 1000

7 0111

0 0000

0 0000

0 0000

0 0000

Agora reescrevemos a sequência binária, de acordo com o padrão de ponto flutuante. sinal 0

expoente 10000011

significando 00001110000000000000000

A interpretação para o sinal é positivo. Para o expoente, temos 100000112 = 13110. Ao subtrairmos o desvio, obtemos 131 − 127 = 4. Com o bit oculto, o significando torna-se 1.0000111. O resultado é +1.0000111 × 24, ou simplesmente 10000.111. Se convertermos a porção à esquerda em decimal, encontraremos 24 = 16. A conversão da porção à direita resulta em 2−1 + 2−2 + 2−3 = .875, de modo que o número original deve ser 16.875, tal como na declaração sprintf anterior.

  1.3   Números Complexos Números complexos são importantes para o processamento digital de sinais. Por exemplo, funções como a transformada de Fourier (fft e ifft) fornecem resultados na forma de números complexos. Esse assunto será abordado em detalhes oportunamente neste livro, mas por enquanto você pode pensar na transformada de Fourier como uma função que converte dados em uma forma interessante. Os números complexos proporcionam um modo conveniente de se armazenar duas informações, sejam elas coordenadas x e y ou uma magnitude (extensão de um vetor bidimensional) e um ângulo (o grau de rotação do vetor). Essa informação possui uma interpretação física em alguns contextos, tal como corresponder à magnitude e ao ângulo de fase de uma senoide para uma dada frequência, quando fornecida pela transformada de Fourier. Você provavelmente deparou-se pela primeira vez com números complexos em alguma aula de matemática, fatorando raízes com a fórmula quadrática. Isto é, uma equação como 2x2 + 4x − 30 = 0 poderia ser fatorada como (x − raiz1) (x − raiz2), em que

Se os números forem propícios, a fórmula quadrática resultará em um par de raízes reais. Em nosso exemplo, elas seriam 3 e −5, resultando em (x − 3)(x + 5) = 0. Um problema surge quando b2 − 4ac é negativo, pois a raiz quadrada de uma quantidade negativa não existe. Espera-se que o operador da raiz quadrada retorne a raiz positiva. Como podemos tomar um número positivo, multiplicá-lo por ele mesmo e terminarmos com um número negativo? Para cada valor real, mesmo os negativos, o quadrado é positivo. Para lidar com isso, temos a quantidade imaginária j. (Algumas pessoas preferem chamá-la de i.) Essa quantidade imaginária é definida:

Os números que possuem j são chamados de números imaginários, bem como de números complexos. Um número complexo possui uma parte real e uma parte imaginária, na forma real + imaginária × j. Por exemplo, x = 3 + 4j. Para especificarmos a parte real ou imaginária, utilizamos funções de mesmo nome, ou seja, Real(x) = 3 e Imaginária(x) = 4, ou simplesmente (x) = 3 e (x) = 4.

Weeks 001.indd 8

14/08/11 19:40

Introdução    9

r

Figura 1.1  Um exemplo de vetor.

A melhor forma de encarar os números complexos é como uma generalização bidimensional dos números; e para os números reais é como se tivessem um componente imaginário zero. Deste modo, os números reais repousam sobre o eixo x. Números complexos podem ser utilizados para conter duas informações. Por exemplo, você provavelmente se lembra da conversão de números com coordenadas polares em cartesianas,1 que são duas formas de se expressar a mesma coisa. Um número complexo pode representar um número em coordenadas polares. A melhor forma de demonstrar isso é graficamente. Quando desejamos desenhar um ponto em um espaço bidimensional, precisamos especificar uma coordenada x e uma coordenada y. Esse par de números nos informa onde o ponto está localizado. Coordenadas polares fazem isso também, com uma diferença: o ponto é especificado por um comprimento e um ângulo. Ou seja, o comprimento especifica um segmento de reta começando no ponto 0 e indo para a direita, enquanto o ângulo informa o grau de rotação do segmento de reta no sentido anti-horário. Da mesma forma, um número complexo representa um ponto nesse plano, onde a parte real e a parte imaginária se combinam para especificar o ponto. A Figura 1.1 exibe um vetor bidimensional com r unidades de comprimento e um ângulo de giro u. Para a conversão de coordenadas polares em cartesianas complexas, utilizamos as seguintes equações [7]: x = r cos(u) y = r sin(u).* A função MATLAB a seguir converte coordenadas polares em coordenadas complexas. O MATLAB será explicado no Capítulo 2, mas qualquer um com alguma experiência em programação será capaz de captar a ideia do código a seguir. % Converta forma (magnitude, angulo) na forma x+jy % % uso: [z] = polar2complex(magnitudes, angulos) function [Z] = polar2complex(mag, angles) % Encontre coordenadas x e y como um numero complexo, % no qual Z = x + jy. Z = mag.*cos(angles) + j*mag.*sin(angles); As duas equações a seguir permitem que convertamos coordenadas cartesianas em polares:

1

Homenagem a René Descartes, o mesmo homem famoso pelo princípio “cogito ergo sum”, ou “penso, logo existo”. * Embora a notação em português seja sen(u), preferimos manter a original uma vez que o MATLAB segue o padrão americano. (N.T.)

Weeks 001.indd 9

14/08/11 19:40

10    CAPÍTULO 1 Eixo imaginário z2 = – a + jb

Eixo imaginário

j

z1 = a + jb

j r1

r2

2

1

Eixo real

Eixo imaginário

Eixo imaginário

j

j 4

3

Eixo real r3 z3 = – a – jb

Eixo real

r4

Eixo real z4 = a – jb

Figura 1.2  O cálculo de u = arctan(b/a) gera um problema quando a é negativo.

Existe um problema com a equação utilizada para a conversão em coordenadas polares. Se tanto a parte real quanto a imaginária (x e y) forem negativas, os sinais se cancelarão e a função arctan retornará o mesmo valor que retornaria caso ambas fossem positivas. Em outras palavras, a conversão de 2 + j2 em coordenadas polares fornece exatamente a mesma resposta que a conversão de −2 − j 2. A função arctan retorna para arctan(y/−x) o mesmo valor que retorna para arctan(−y/x). Para resolver esse problema, examine a parte real (x) e, se ela for negativa, adicione p (ou 180º) ao ângulo. Observe que a subtração de p também funciona, visto que o ângulo é deslocado em p e as funções são periódicas em 2p. Ou seja, as funções seno e cosseno retornam para um argumento u + p − 2p os mesmos resultados que retornariam para um argumento u + p e u + p − 2p = u − p. A Figura 1.2 demonstra isso. Um vetor é exibido em todos os quatro quadrantes. O raio r é igual a 2 x + y 2 , e, uma vez que x é sempre a positivo ou negativo e y é sempre b positivo ou negativo, todos os quatro valores de r são os mesmos, em que a e b correspondem às distâncias da origem nos eixos real e imaginário, respectivamente. Considere a = 3 e b = 4:

Eis os ângulos calculados:

Claramente, u2 e u3 não estão corretos, pois repousam no segundo e terceiro quadrantes, respectivamente. Assim, seus ângulos devem estar entre p/2 (< 1.57) e p (< 3.14) para u2 e entre −p/2 e −p para u3. A adição de p a u2 e −p a u3 resolve o problema. u4 está correto, muito embora a função arctan retorne um valor negativo para ele. Eis os ângulos corrigidos:

Weeks 001.indd 10

14/08/11 19:40

Introdução    11

A função a seguir converte números complexos como x + jy em coordenadas polares. Ela não é tão eficiente quanto o uso de abs e angle, mas demonstra como implementar as equações. % % Converta forma complexa (x+jy) % em forma polar (magnitude, angulo % % uso: [r, theta] = complex2polar(X) % function [mag, phase] = complex2polar(X) % Calcule magnitudes mag = sqrt(real(X).*real(X) + imag(X).*imag(X)); % Encontre angulos de fase % Note que os parametros para tan e atan estao em radianos for i=1:length(X) if (real(X(i)) > 0) phase(i) = atan(imag(X(i)) / real(X(i))); elseif (real(X(i)) < 0) % Add to +/- pi, depending on quadrant of the point if (imag(X(i)) < 0) % then we are in quadrant 3 phase(i) = -pi + atan(imag(X(i)) / real(X(i))); else % we are in quadrant 2 phase(i) = pi + atan(imag(X(i)) / real(X(i))); end else % Se a parte real for 0, repousara sobre a parte + ou – do eixo y % dependendo do sinal da parte imaginaria phase(i) = sign(imag(X(i)))*pi/2; end end

  1.4   O que É um Sinal? Um sinal é um fenômeno variável que pode ser medido. Muitas vezes trata-se de uma quantidade física que varia com o tempo, embora também possa variar com outro parâmetro, tal como o espaço. Exemplos incluem o som (ou, mais precisamente, a pressão acústica), uma tensão (tal como as diferenças de tensão produzidas por um microfone), radar e imagens transmitidas por uma câmera de vídeo. A temperatura é outro exemplo de sinal. Medida a cada hora, a temperatura flutuará, indo normalmente de um valor baixo (ao amanhecer) para um valor mais alto (no final da manhã), até um valor ainda maior (à tarde) e depois para um valor mais baixo (ao anoitecer), até finalmente atingir novamente um valor baixo à noite. Em muitos casos, devemos examinar o sinal ao longo de um período de tempo. Se, por exemplo, você estiver planejando viajar para uma cidade distante, saber a temperatura média na cidade pode lhe dar uma noção das roupas a serem postas na mala. Mas, se você verificar como a temperatura muda ao longo de um dia, poderá saber se precisará ou não levar uma jaqueta.

Weeks 001.indd 11

14/08/11 19:40

12    CAPÍTULO 1 Os sinais podem conter erros devido às limitações do dispositivo de medição ou devido ao ambiente. Um sensor de temperatura, por exemplo, pode ser afetado por um vento frio. Na melhor das hipóteses, os sinais representados por um computador constituem boas aproximações dos processos físicos originais. Alguns sinais reais, como a temperatura, podem ser medidos continuamente. Não importa por quanto tempo você olhar para um termômetro, ele fornecerá uma leitura, mesmo que o tempo entre as leituras seja arbitrariamente curto. Podemos registrar a temperatura em intervalos de um segundo, um minuto, uma hora etc. Uma vez que tenhamos registrado essas medições, compreendemos intuitivamente que a temperatura possui valores entre as leituras e que não sabemos quais seriam eles. Se soprar um vento frio, a temperatura cairá e, se o sol brilhar entre as nuvens, ela subirá. Suponha, por exemplo, que meçamos a temperatura a cada hora. Ao fazermos isso, estamos optando por ignorar a temperatura o tempo todo exceto durante as leituras de hora em hora. Trata-se de uma ideia importante: o sinal pode variar ao longo do tempo, mas, quando fazemos leituras periódicas do sinal, terminamos apenas com uma representação do mesmo. Um sinal pode ser imaginado como uma sequência (contínua ou discreta) de valores (contínuos ou discretos). Ou seja, um sinal contínuo pode ter valores em qualquer valor de índice (index) arbitrário (você pode medir a temperatura ao meio-dia ou, caso deseje, pode medi-la 0.0000000003 segundo após o meiodia). Um sinal discreto, entretanto, possui restrições quanto ao índice – normalmente, a de que ele deve ser um inteiro. Por exemplo, a massa de cada planeta em nosso sistema solar poderia ser registrada, numerando-se os planetas de acordo com suas posições relativas a partir do sol. Para simplificar, presumese que um sinal discreto possua um índice inteiro e que a relação entre o índice e o tempo (ou qualquer que seja o parâmetro) seja fornecida. Da mesma forma, os valores para o sinal podem ser medidos com uma precisão arbitrária (contínua) ou com uma precisão limitada (discreta). Isto é, você poderia registrar a temperatura em milionésimos de grau ou poderia limitar os valores a um nível razoável, tal como um dígito além do decimal. Discreto não significa inteiro, e sim que os valores poderiam ser armazenados como um número racional (um inteiro dividido por outro inteiro). Por exemplo, 72.3 graus Fahrenheit poderiam ser encarados como 723/10. Isso implica que números irracionais não podem ser armazenados em um computador, mas apenas aproximados. Um bom exemplo é o p. Você pode escrever 3.14 para representar o p, mas trata-se de uma mera aproximação. E, se você escreveu 3.141592654 para representar p, ainda assim não passou de uma aproximação. Na verdade, você poderia representá-lo com 50 milhões de dígitos e mesmo assim continuaria sendo somente uma aproximação! É possível considerar um sinal cujo índice seja contínuo e cujos valores sejam discretos, tal como o número de pessoas presentes em um edifício em um dado momento. O índice (tempo) pode ser medido em frações de segundo, enquanto o número de pessoas é sempre um número inteiro. Também é possível lidar com um sinal em que o índice seja discreto e os valores sejam contínuos; por exemplo, a hora do nascimento de cada pessoa em uma cidade. A pessoa nº 4 pode ter nascido apenas 1 microssegundo antes da pessoa nº 5, mas tecnicamente elas não nasceram ao mesmo tempo. Isso não significa que duas pessoas não possam ter a mesma hora de nascimento, mas que podemos ser tão precisos quanto desejemos em relação a essa hora. A Tabela 1.4 fornece alguns sinais de exemplo, com índices e quantidades medidas contínuas e discretas. Na maioria dos casos, concentraremos nossa atenção nos sinais contínuos (que possuem um índice contínuo e um valor contínuo) e nos sinais discretos (com um índice inteiro e um valor discreto). A maior parte dos sinais na natureza é contínua, mas os sinais representados no interior de um computador são discretos. Um sinal discreto frequentemente é uma aproximação de um sinal contínuo. Uma convenção notacional que adotamos aqui é a utilização de x[n] para um sinal discreto e x(t) para um sinal contínuo. Isso é útil, visto que você provavelmente já está familiarizado com os parênteses para funções matemáticas e com os colchetes para listas (arrays) em muitas linguagens computacionais.

Tabela 1.4

Quantidade medida Discreta Contínua

Weeks 001.indd 12

Sinais de exemplo.

Discreto massa (número do planeta) hora de nascimento (pessoa)

Índice

Contínuo pessoas em um edifício (tempo) temperatura (tempo)

14/08/11 19:40

Introdução    13

Deste modo, existem quatro tipos de sinais:

• Um sinal pode ter um valor contínuo para um índice contínuo. O mundo real está repleto de tais

sinais, mas precisamos aproximá-los caso desejemos que um computador digital trabalhe com eles. Funções matemáticas também seguem o padrão contínuo/contínuo. • Um sinal pode ter um valor contínuo para um índice discreto. • Um sinal pode ter um valor discreto para um índice contínuo. • Um sinal pode ter um valor discreto para um índice discreto. Normalmente esse é o caso em um computador, já que ele só pode lidar com números limitados em escala. Um computador poderia calcular o valor de uma função (por exemplo, sin(x)) ou poderia armazenar um sinal em uma lista (indexada por um inteiro positivo). Tecnicamente, ambos os sinais seguem o padrão discreto/discreto. Concentraremos nossa atenção nos sinais dos tipos contínuo/contínuo e discreto/discreto, pois são os que encontramos no mundo real e no mundo computacional, respectivamente. Doravante nos referiremos a esses sinais como analógicos e digitais, respectivamente. No âmbito digital, um sinal não é mais do que uma lista de números. Ele pode ser encarado como um vetor, uma matriz unidimensional. Naturalmente, existem sinais multidimensionais, tais como as imagens, que são simplesmente matrizes bidimensionais. Encarar os sinais como matrizes constitui uma importante etapa analítica, por permitir que utilizemos álgebra linear com nossos sinais. O significado desses números depende da aplicação e da diferença de tempo entre os valores. Ou seja, uma lista de números poderia corresponder às mudanças na pressão acústica medida em intervalos de 1 milissegundo ou poderia ser a temperatura em graus centígrados medida a cada hora. Essa informação essencial não é armazenada no interior do sinal, mas precisa ser conhecida para que o sinal faça sentido. Os sinais frequentemente são estudados em termos de tempo e amplitude. A amplitude é utilizada como uma forma geral de rotulagem das unidades de um sinal, sem estar limitada pelo sinal específico. Quando se fala da amplitude de um valor em um sinal, não importa se esse valor é medido em graus centígrados, pressão ou tensão. Neste texto, geralmente serão utilizados parênteses para sinais analógicos e colchetes para sinais digitais. Além disso, t normalmente será uma variável contínua denotando tempo, enquanto n será uma variável discreta, representando números de amostra. Ela pode ser encarada como um índice, ou índice da lista (array offset). Assim, x(t) representará um sinal contínuo, mas x[n] representará um sinal discreto. Poderíamos permitir que t assumisse valores como 1.0, 2.345678 e 6.00004, mas n estaria limitado a inteiros positivos como 1, 2 e 6. Em teoria, n poderia ser negativo, embora o limitemos a inteiros positivos {0, 1, 2, 3 etc.} a menos que haja uma razão convincente para utilizarmos negativos também. Os exemplos de programação a seguir apresentam um caso em que essas convenções não serão seguidas. Em MATLAB, assim como em outras linguagens de programação, os colchetes e os parênteses possuem significados diferentes e não podem ser empregados de forma intercambiável. Qualquer leitor que entenda programação não deverá ter problemas com sinais. Normalmente, quando falamos sobre um sinal, estamos nos referindo a algo concreto, tal como mySignal no programa C++ a seguir. #include using namespace std; int main() { float mySignal[] = int n, N=5;

4.0, 4.6, 5.1, 0.6, 6.0 ;

// Imprima os valores do sinal for (n=0; n mySignal mySignal = 4.0000

4.6000

5.1000

0.6000

6.0000

Observe que a versão MATLAB é muito mais compacta, dispensando a declaração inicial de variáveis. Para simplificar, a maior parte dos códigos presentes neste livro está em MATLAB.

  1.5   Analógico versus Digital Conforme mencionado anteriormente, existem dois tipos de sinal: analógico e digital. A palavra “analógico” está relacionada à palavra “analogia”; um sinal contínuo (do mundo real) pode ser convertido em uma forma diferente, tal como a cópia analógica exibida na Figura 1.3. Nela visualizamos uma representação da pressão do ar captada por um microfone ao longo do tempo. Um gravador de fita cassete conectado ao microfone produz uma cópia desse sinal ajustando a magnetização na fita magnética à medida que ela passa sob a cabeça de leitura/gravação. Deste modo, obtemos uma cópia do sinal original (pressão do ar variando com o tempo) na forma de magnetização ao longo da extensão de uma fita. Naturalmente, podemos ler essa fita cassete mais tarde: o magnetismo da fita em movimento afeta a eletricidade que passa através da cabeça de leitura/gravação e que pode ser amplificada e passada para os alto-falantes, os quais convertem as variações elétricas em variações de pressão do ar, reproduzindo o som. Um sinal analógico é aquele que possui valores contínuos, ou seja, uma medida pode ser coletada em qualquer instante arbitrário e com o nível de precisão desejado (ou, no mínimo, com a precisão permitida pelo dispositivo de medição). Sinais analógicos podem ser expressos em termos de funções. Um sinal bem entendido pode ser expresso com precisão por meio de uma função matemática ou aproximadamente por meio de uma função semelhante caso a aproximação seja suficiente boa para a aplicação. Uma função

Pressão do ar

Original (som)

Tempo

Magnetização

Cópia analógica

Espaço (posição da fita) Figura 1.3  Um sinal sonoro com uma fita analógica.

Weeks 001.indd 14

14/08/11 19:40

Introdução    15

matemática é bastante compacta e de fácil utilização. Suponha que tenhamos um sinal analógico descrito por x(t). A variável de tempo t é entendida como contínua, isto é, ela pode assumir qualquer valor: t = −1 é válido, tal como t = 1.23456789. Um sinal digital, por outro lado, possui valores discretos. Pode-se obter um sinal digital a partir de um analógico através de um processo conhecido como amostragem (sampling), no qual os valores são medidos (amostrados) em intervalos regulares e armazenados. Para um sinal digital, os valores são acessados por meio de um índice, normalmente um valor inteiro. Em um exemplo de sinal digital, x[n], a variável n relaciona-se com t na equação t = nTs, sendo Ts o tempo de amostragem. Se você medir a temperatura ao ar livre a cada hora, o seu tempo de amostragem será Ts = 1 hora e você coletará uma medida em n = 0 (0 hora, o instante inicial), depois novamente em n = 1 (1 hora), em n = 2 (2 horas), em n = 3 (3 horas), e assim por diante. Desta maneira, o sinal é quantificado (ou “quantizado”) no tempo, significando que só dispomos de valores para o sinal em momentos específicos. O tempo de amostragem não precisa ser um valor inteiro na verdade, sinais medidos em milissegundos, tais como Ts = 0.001 segundo, são bastante comuns. Com esse tempo de amostragem, o sinal será medido a cada nTs segundos: 0 segundo, 0.001 segundo, 0.002 segundo, 0.003 segundo etc. Observe que n, nosso índice, ainda é um inteiro, assumindo os valores 0, 1, 2, 3, e assim por diante. Um sinal medido em Ts = 0.001 segundo ainda é quantificado no tempo. Muito embora tenhamos medições em 0.001 segundo e 0.002 segundo, não temos uma medição em 0.0011 segundo. A Figura 1.4 exibe um exemplo de amostragem. No traçado superior, temos uma curva contínua (simulada) mostrada ao longo do tempo. No traçado central temos a operação de amostragem, que equivale a multiplicar a curva por um conjunto de impulsos em intervalos de 0.02 segundo. O traçado inferior mostra nosso sinal digital resultante, em termos de um número da amostra. Naturalmente, o número da amostra está diretamente relacionado ao tempo (neste exemplo), mas, para que isso signifique alguma coisa, temos de lembrar o tempo entre intervalos. Neste texto, nosso número da amostra terá início em zero, tal como indexaríamos uma lista em C/C++. Entretanto, acrescentaremos um ao índice em código MATLAB, pois o MATLAB indexa listas começando em 1.

Exemplo de sinal contínuo 1 0 –1 –2 0

0.02

0.04

0.06

0.08

0.1

0.12

0.14

0.16

0.18

0.2

0.14

0.16

0.18

0.2

7

8

9

10

Operação de amostragem

1

0.5 –1 0 0

0.02

0.04

0.06

0.08

0.1

0.12

Sinal digital resultante

2 1 0 –1 –2 0

1

2

3

4

5

6

Figura 1.4  Amostragem de um sinal contínuo.

Weeks 001.indd 15

14/08/11 19:40

16    CAPÍTULO 1 Suponha que o sinal digital x tenha os valores x[1] = 2 e x[2] = 4. Podemos concluir que x[1.5] = 3? Trata-se de um problema, pois não há valor para x[n] quando n = 1.5. Qualquer interpolação efetuada nesse sinal deve ser processada com muito cuidado! Embora x[1.5] = 3 possa ser uma boa estimativa, não podemos concluir que esteja correta (no mínimo, precisamos de mais informações). Simplesmente não dispomos de uma medida coletada naquele momento. Os sinais digitais também são quantificados em amplitude. Quando um sinal é amostrado, armazenamos os valores em memória. Cada posição de memória possui um grau de precisão finito. Se o número a ser armazenado for muito grande ou muito pequeno para caber na posição de memória, um valor truncado será armazenado em seu lugar. Para usarmos uma analogia, considere uma bomba de gasolina. Ela pode exibir um total de cinco dígitos para o custo da gasolina: três dígitos para o valor em reais e dois dígitos para os centavos. Se você possuísse um grande caminhão, o abastecesse com o equivalente a R$ 999,99 de gasolina e bombeasse um pouquinho a mais (digamos, o equivalente a dois centavos), a bomba de gasolina mostraria algo como R$ 000,01, pois o valor seria um número muito alto para que ela conseguisse exibir. Do mesmo modo, se você fosse a um posto de gasolina e abastecesse com o equivalente a uma fração de um centavo, o valor seria um número muito baixo para ser exibido na bomba e provavelmente continuaria em R$ 000,00. Tal como o mostrador em uma bomba de gasolina, a memória de um dispositivo digital possui um grau de precisão finito. Um valor armazenado em memória não pode ser muito grande ou muito pequeno, ou o montante efetivamente armazenado será reduzido de modo a corresponder à capacidade da memória. Isso é o que se entende por quantificação (ou “quantização”) em amplitude. A propósito, como “muito pequeno” nesse contexto significa um montante fracionário abaixo da capacidade de armazenamento da memória, um número poderia ser muito grande e muito pequeno ao mesmo tempo. Por exemplo, a bomba de gasolina mostraria R$ 000,00 mesmo que o custo real do abastecimento fosse R$ 1000,004. Considere o código C/C++ a seguir, que ilustra uma questão acerca da precisão. unsigned int i; i=1; while (i != 0) { /* algum outro código */ i++; } Esse código parece ser um loop infinito, pois a variável i começa maior que 0 e não para de crescer. Se executarmos esse código, entretanto, ele terá um fim. Para vermos por que isso acontece, examinaremos a representação interna da variável i. Suponha que nosso computador possua um tamanho de palavra de somente 3 bits e que o compilador utilize esse tamanho de palavra para inteiros. Não se trata de uma situação realística, mas a ideia é válida mesmo que o tamanho de palavra seja de 32, 64 ou 128 bits. A variável i começa no valor decimal 1, que, naturalmente, corresponde a 001 em binário. À medida que ela aumenta, torna-se 010, depois 011, 100, 101, 110 e, por fim, 111. A adição de 1 resulta em 1000 em binário, mas o 1 à esquerda configura um overflow, pois utilizamos somente 3 bits para os inteiros e 000 será armazenado em i. Assim, a condição de finalização do loop while é atendida. Note que esse também seria o caso se removêssemos as palavras-chave unsigned, pois os valores anteriores para i seriam os mesmos – só o que mudaria seria nossa interpretação do número decimal que ele representa. A Figura 1.5 mostra três aparências possíveis para um sinal: como um sinal analógico, um sinal digital e um sinal analógico baseado na versão digital. Ou seja, podemos ter um sinal do mundo real que digitalizamos e processamos em um computador e que, em seguida, convertemos de volta para o mundo real. Na parte superior, a figura mostra como poderia ser a aparência de um sinal analógico simulado se o visualizássemos em um osciloscópio. Se dispuséssemos de uma representação abstrata para ele, chamaríamos de sinal analógico x(t). No gráfico central dessa figura, vemos como seria a aparência do sinal se o medíssemos a cada 10 milissegundos. O sinal digital consiste em uma coleção de pontos, indexados pelo número de amostra n. Deste modo, poderíamos denotar esse sinal como x[n]. Na parte inferior dessa figura, vemos uma versão reconstruída do sinal, baseada nos pontos digitais. Precisamos de um novo rótulo para ~ esse sinal – talvez x9(t), x (t) ou xˆ(t), mas esses símbolos possuem outros significados em contextos diferentes. Para evitar confusão, podemos atribuir nomes a todos eles, tais como x2(t), xreconstruído(t) ou novo–x(t). Embora o sinal reconstruído tenha aproximadamente a mesma forma do sinal original (analógico), as diferenças são visíveis. Nesse exemplo simplesmente conectamos os pontos, embora existam outras maneiras (melhores) de se reconstruir o sinal.

Weeks 001.indd 16

14/08/11 19:40

Introdução    17

Sinal analógico

2 1 0 –1 –2 0

0.02

0.04

0.06 Tempo (segundos)

0.08

Sinal digital

2 1

*

–1

0.12

*

*

*

0

–2 0 Sinal reconstruído

*

0.1

*

*

*

*

* 2

4

0.02

0.04

6 Número da amostra

*

* 8

10

*

12

2 1 0 –1 –2 0

0.06 0.08 Tempo (segundos)

0.1

0.12

Figura 1.5  Três modos de se visualizar um sinal.

É possível converter de analógico em digital e de digital em analógico. O processamento analógico de sinal emprega componentes eletrônicos como resistores, capacitores, amplificadores operacionais etc. Sua implementação é mais econômica, pois essas peças tendem a ser de baixo custo. Já no DSP utilizamos elementos multiplicadores, somadores e de retardo (registradores) para processar o sinal. O DSP é mais flexível. Por exemplo, a detecção e a correção de erros podem ser facilmente implementadas em DSP.

  1.6   O que É um Sistema? Um sistema é algo que executa uma operação (ou uma transformação) em um sinal. Um sistema pode ser um dispositivo físico (hardware) ou um software. A Figura 1.6 mostra uma abstração de um sistema – simplesmente uma caixa preta que produz saída y[n] baseada em uma entrada x[n]. Um exemplo simples de sistema consiste em um incrementador que adiciona 1 a cada valor do sinal. Ou seja, suponha que o sinal x[n] seja {1, 2, 5, 3}. Se y[n] = x[n] + 1, então y[n] seria {2, 3, 6, 4}.

x[n]

Sistema

y[n]

Figura 1.6  Um exemplo de sistema.

São exemplos de sistemas os transformadores de Fourier diretos/inversos (consulte o Capítulo 6, “A Transformada de Fourier”) e filtros tais como o Resposta Finita ao Impulso (Finite Impulse Responce – FIR) e o Resposta Infinita ao Impulso (Infinite Impulse Responce – IIR) (consulte o Capítulo 3, “Filtros”).

Weeks 001.indd 17

14/08/11 19:40

18    CAPÍTULO 1

  1.7   O que É uma Transformada? Uma transformada ou transformação* consiste na operação que um sistema executa. Assim, sistemas e transformadas estão intimamente ligados. Uma transformada pode possuir um inverso, que restaura os valores originais. Por exemplo, segundo um velho chavão, um otimista enxerga um copo de água pela metade como meio cheio, enquanto um pessimista o enxerga como meio vazio. A quantidade de água no copo não muda – somente a forma como é representada. Suponha que um pessimista registre a quantidade de água em três copos, cada qual com a capacidade de 1 litro, como 50% vazio, 75% vazio e 63% vazio. Qual o volume de água total? Para responder a essa pergunta, seria fácil somar a quantidade de água em cada copo, mas não dispomos dessa informação. Em vez disso, temos medidas da capacidade presentes em cada um deles. Se pusermos os dados na forma de “transformada do otimista”, y[n] = 1 − x[n], os dados irão se tornar 50%, 25% e 37%. Ao somarmos esses números teremos 112% de um litro, ou 1,12 litro de água. Uma transformada pode ser encarada como um modo diferente de se representar a mesma informação. Quão cheios estão os copos de água na Figura 1.7? Um otimista diria que eles estão, respectivamente, meio cheio, um quarto cheio e um décimo cheio. Um pessimista diria que o copo está 1 – (otimista) vazio, ou seja, 0,5 vazio, 0,75 vazio e 0,90 vazio. Assim, as duas maneiras de especificar a mesma informação podem ser convertidas em ambas as direções. Às vezes, um modo de encarar a informação é melhor do que outro. Por exemplo, constatamos que o modo do otimista de ver as coisas nos permite saber a quantidade total de água em todos os três copos, facilitando o trabalho. Entretanto, se você precisasse calcular a quantidade de água a ser acrescentada em cada copo para enchê-lo, o modo de medição de quantidades do pessimista seria melhor.

Figura 1.7  Três copos d’água.

Suponha que tenhamos um sistema incrementador simples que adicione 1 a cada entrada. Utilizaremos x1[n] para representar o sinal de entrada e y1[n] para representar a saída. Podemos escrever a saída em termos da entrada, ou seja, y1[n] = x1[n] + 1. Se x1 for a sequência {1, 3, 7}, a saída y1 será a sequência {2, 4, 8}. Suponha agora que desejemos obter de volta os valores originais de x1. Para tal, precisamos desfazer a transformada, o que pode ser conseguido com a transformada inversa a seguir. Como já demos um nome para a transformada reversa, chamaremos a primeira transformada (y1[n] = x1[n] + 1) de direta. A transformada inversa para o sistema incrementador seria um decrementador, que simplesmente subtrai 1 de cada valor, ou seja, y2[n] = x2[n] − 1. Se interligássemos esses dois sistemas em uma série, teríamos a saída do primeiro sistema como a entrada do segundo, atribuindo efetivamente x2[n] = y1[n]. Assim, y2[n] = x2[n] − 1, y2[n] = y1[n] − 1 ou y2[n] = (x1[n] + 1) − 1. É fácil perceber que os dois sistemas cancelam um ou outro e a saída final (y2[n]) é aquela com a qual começamos originalmente (x1[n]). Normalmente, não estudamos sistemas que adicionam ou subtraem constantes. Mas essa ideia é válida com um sistema que multiplica a entrada por dois, seguido de um sistema que divide cada valor por dois. Os dois sistemas em série executam uma transformada direta e inversa, sem efeito global no sinal original. A pergunta natural é “por que faríamos isso?”. Uma resposta seria: para que possamos analisar o sinal transformado ou comprimi-lo. Isso não resulta de um sistema simples, porém transformadas mais complicadas (tal como a transformada discreta de cosseno) têm sido utilizadas de maneira efetiva para

* Embora transformação esteja igualmente correto, o termo transformada de Fourier é bem mais difundido. (N.T.)

Weeks 001.indd 18

14/08/11 19:40

Introdução    19

permitir que um programa de compressão altere automaticamente o sinal de modo a armazená-lo de forma compacta. Às vezes transformadas são executadas porque as coisas se tornam mais fáceis no âmbito transformado. Suponha, por exemplo, que você desejasse descobrir o número de anos entre os filmes Guerra nas Estrelas (copyright MCMLXXVII) e Guerra nas Estrelas: Episódio II – Ataque dos Clones (copyright MMII). Subtrair um número do outro não é um problema, mas trabalhar com numerais romanos é uma tarefa complicada [8]! O algoritmo que utilizamos para subtrair os dígitos da coluna mais à direita primeiro, escrever a diferença e tomar emprestado a partir da esquerda não funciona. Em vez disso, a coisa mais fácil a se fazer é converter os numerais romanos em números decimais, efetuar a subtração e converter o resultado de volta em numerais romanos. Nesse caso, a transformada converteria MCMLXXVII em 1977 e MMII em 2002. A subtração de 1977 de 2002 resulta 25, que é transformado inversamente em XXV. A execução da transformada e a posterior transformada inversa podem parecer uma perda de tempo, mas não se isso simplificar substancialmente as etapas intermediárias. Se você aprendeu equações diferenciais em algum curso, pode ter visto a transformada de Laplace sendo empregada com este propósito: proporcionar uma maneira fácil de resolver essas equações. Após a montagem da equação, você aplica a transformada de Laplace (muitas vezes consultando uma tabela) e, em seguida, a equação transformada pode ser manipulada e resolvida utilizando-se álgebra. Por fim, o resultado é transformado inversamente de volta ao domínio do tempo (novamente, muitas vezes consultando-se uma tabela). A transformada de Laplace também tem utilidade na determinação da estabilidade de sistemas, e esse é um assunto ao qual retornaremos em um capítulo posterior.

  1.8   A Transformada Aditiva/Subtrativa A transformada aditiva/subtrativa é simples e é utilizada para explicar conceitos-chave da transformada de wavelet. Suponha que tenhamos o seguinte sinal de exemplo: {4, 7, 1, 8}. Tomaremos cada par de números, somaremos os números entre si e subtrairemos o segundo do primeiro. Para as adições/somas, teremos 4 − 7 e 1 + 8, resultando nos números {11, 9}. Para as subtrações/diminuições, encontraremos 4 − 7 e 1 − 8. A ordem é importante: sempre subtraímos o segundo número do primeiro de cada par. Os números {−3, −7} resultam das subtrações. Portanto, nossa transformação nos fornece dois sinais: 11, 9 e −3, −7. Como podemos desfazer a transformada? Suponha que tenhamos esquecido os números originais, mas que nos lembremos de como os obtivemos. Rotularemos a sequência original como {x 1, x 2, x 3, x 4}. A partir da transformada, sabemos que: x 1 + x 2 = 11 x 1 − x 2 = −3. A soma dessas duas equações nos fornece: 2(x 1) + (x 2 − x 2) = 11 + (−3)  portanto, x 1 = (11 − 3)/2 = 4. Podemos aplicar esse número em qualquer uma das equações anteriores que possua tanto x 1 quanto x 2. Vamos utilizar a primeira: 4 + x 2 = 11 x 2 = 11 − 4 = 7. Assim, concluímos que os dois primeiros números originais são {4, 7}. Para encontrarmos o restante dos números originais, repetimos a lógica anterior nos próximos números provenientes de nossos sinais. Deixamos isso como um exercício para o leitor. Vimos como o sinal de exemplo {4, 7, 1, 8} corresponde aos dois sinais {11, 9} e {−3, −7}. Suponha que encontremos a transformada aditiva/subtrativa em cada um destes sinais resultantes:

Weeks 001.indd 19

14/08/11 19:40

20    CAPÍTULO 1 11 + 9 = 20 11 − 9 = 2  e −3 + (−7) = −10 −3 − (−7) = 4. Isso resulta em quatro sinais: {20}, {2}, {−10} e {4}. Não podemos ir além, ou, ao menos, não há razão para ir além devido ao nosso volume de dados. Em nossa transformada aditiva/subtrativa,2 acrescentamos as entradas em pares e em seguida pulamos para os próximos pares de entrada. Pense nesse procedimento como o deslocamento da transformada em relação aos dados. Primeiro encontramos 4 + 7 e depois deslocamos para encontrar 1 + 8 etc. Poderíamos ter uma transformada que envolvesse um grupo maior de números, mas ainda assim deslocaríamos de dois em dois para cada cálculo. Cada nível afeta um pouco mais o sinal (escalonamento). Por exemplo, o nível 1 computa 4 + 7 = 11, envolvendo os dois primeiros números. O nível 2 computa (4 + 7) + (1 + 8) = 20, em que utilizamos o sinal integral. Mesmo que tivéssemos um sinal mais longo, chegaríamos eventualmente a um nível em que utilizaríamos o sinal integral. Essa transformada simples mostra o deslocamento e o escalonamento (do índice) que existe na transformada de wavelet. Para resumir, temos uma aproximação de 20 e uma média de 20/4. Temos dois detalhes – nível 1: {−3, −7} e nível 2: {2}. Mais tarde examinaremos uma semelhança com a transformada discreta de wavelet (DWT); ela possui 1 sinal de aproximação e J sinais de detalhe, em que J baseia-se na extensão da entrada. Se tivéssemos fornecido à DWT um sinal de quatro valores, esperaríamos somente duas oitavas de resolução possíveis, pois 22 = 4. Isso nos leva a uma pergunta natural: “por que devemos nos importar com a transformada aditiva/subtrativa?”. Para responder a tal pergunta, observe primeiramente que poderíamos multiplicar diretamente por para eliminar a necessidade de dividir ao final (ou seja, 20/4). Se fizermos isso, nossa transformada aditiva/subtrativa irá se tornar a transformada de Haar (de 1910). A operação de adição corresponde à função de escalonamento f, enquanto a operação de subtração executa a função de wavelet . (Exploraremos esses conceitos detalhadamente em capítulos posteriores.) Com uma ligeira mudança em nossa transformada aditiva/subtrativa, percebemos que ela utiliza coeficientes e Uma wavelet é uma pequena onda, deslocada e escalonada, utilizada para representar um sinal como ondas grandes, médias e pequenas. A transformada aditiva/subtrativa também faz isso.

  1.9   Operações com Senoides Por que estudamos senoides ao tratarmos do processamento digital de sinais? Examinamos as funções senoidais (seno e cosseno) por se tratarem de funções interessantes que frequentemente aparecem no mundo analógico. O exame de uma única função cosseno é bem simples, e o que se aplica a uma função cosseno também se aplica a um sinal composto de diversas senoides. Uma vez que as funções senoidais ocorrem com frequência, a capacidade de utilizar alguns dados para representar uma senoide acaba sendo vantajosa. Quase todas as senoides neste texto serão do seguinte formato: amplitude × cos(2p × frequência × t + fase). Isso é tudo! Quando a amplitude, a frequência e a fase são conhecidas, temos toda a informação de que precisamos para encontrar o valor dessa senoide para qualquer valor de tempo (t). Trata-se de uma forma bastante compacta de se representar um sinal. Se o sinal for mais complexo – digamos que ele seja composto de duas senoides –, precisaremos apenas das informações de amplitude, frequência e fase das duas senoides. Deste modo, podemos representar o sinal e recompô-lo mais tarde a partir dessas informações. Pense em como a Terra gira em torno do Sol ao longo de um ano. A Terra recebe a mesma quantidade de luz solar todos os dias, mas sua inclinação faz com que um dos hemisférios receba mais sol do que o outro. A partir do solstício de dezembro (em torno do dia 22), o hemisfério Norte da Terra recebe

2

Weeks 001.indd 20

Veja o programa doNundoPM.m no site da LTC Editora.

14/08/11 19:40

Introdução    21

a menor quantidade de luz solar [9]. À medida que a Terra gira em torno do Sol, esse hemisfério recebe uma quantidade crescente de luz solar (primavera), até atingir o máximo (verão). A partir daí, os dias vão ficando mais curtos (outono) e, finalmente, retornam ao ponto baixo novamente no inverno. As estações variam com o tempo. Em duas dimensões, seria possível diagramar isso com o Sol no centro e a Terra percorrendo um caminho circular em torno dele (aproximadamente). Essa informação também pode ser visualizada representando-se graficamente a distância com o tempo como o eixo x. Para o eixo y, considere a distância horizontal entre os dois. O gráfico resultante se parecerá com uma onda senoidal. As estações mudam com o tempo, mas sempre se repetem. Portanto, esse sinal é periódico. Exemplos de sinais senoidais estão por toda parte. A rotação da Lua em torno da Terra, a temperatura de uma cidade ao longo de um dia e as ondas acústicas criadas pela voz humana constituem todas exemplos de sinais de forma senoidal. A voz humana é interessante, pois pode ser aproximada com poucas senoides (tal como o som de uma vogal) ou com um sinal composto de muitas senoidais.

  1.9.1   Traçado de Senoides Os sinais compostos são importantes tanto na teoria quanto na prática. Qualquer função que não seja verdadeiramente aleatória pode ser aproximada com uma soma de senoides. Em outras palavras, um sinal pode ser decomposto em uma combinação de sinais mais simples. Como uma abstração matemática, o sinal composto x(t) = cos(2p100t) + cos(2p200t) pode ser facilmente traçado em MATLAB. O exemplo a seguir funcionará, embora seja ineficiente. Ts = 0.0002; % o tempo de amostragem, Ts, e de 0.2 ms for n = 1:100 % teremos 100 amostras x(n) = cos(2*pi*100*n*Ts) + cos(2*pi*200*n*Ts); end plot(x); O código anterior soma os dois cossenos, ponto a ponto, e armazena os resultados em um vetor x [7] [10] [11]. Podemos utilizar n em lugar de 1:length(x) para esse exemplo em particular, caso desejemos. Além disso, a utilização de x funcionaria tão bem aqui quanto x(n). Um modo mais eficiente consiste em eliminar o loop for, conforme mostrado a seguir. Ts = 0.0002; % o tempo de amostragem, Ts, e de 0.2 ms n = 1:100; % teremos 100 amostras x = cos(2*pi*100*n*Ts) + cos(2*pi*200*n*Ts); plot(n, x, b); Note que esse exemplo MATLAB não traça efetivamente x(t). Ele traça x(t = nTs), sendo n um inteiro entre 1 e 100. Ts é o tempo de amostragem, um valor importante que examinaremos com mais detalhes em um capítulo posterior. Poderíamos simular x(t), mas ele ainda seria um sinal digital, visto que a variável t possui um conjunto limitado de valores. O código de exemplo parece-se com este: % x(t) simulado t = 0:0.0002:0.02; x = cos(2*pi*100*t) + cos(2*pi*200*t); plot(1:length(x), x, b); O parâmetro 1:length(x) especifica uma lista que possui o mesmo número de elementos que x, o que pode ser visto ao longo do eixo x. Um exemplo final do comando de traçado é: plot(t, x, b);

Weeks 001.indd 21

14/08/11 19:40

22    CAPÍTULO 1 Observe como o eixo x agora reflete o tempo em vez do número da amostra. Quando diversos componentes senoidais estão no mesmo sinal e cada uma das frequências é um múltiplo inteiro de uma frequência comum, dizemos que as frequências são harmonicamente correlatas. Uma função verdadeiramente aleatória não pode ser bem representada com uma soma de senoides. Tal função não se repetiria, mas prosseguiria indefinidamente como um valor imprevisível para qualquer valor de tempo. Isso é semelhante a um número irracional. A maioria dos valores é racional, ou seja, pode ser representada como uma divisão de um número inteiro por outro – por exemplo, a grandeza 1,5 é um número racional, pois é o mesmo que 3/2. Ela também poderia ser representada como 15/10, se desejado, assim como 1,4 poderia ser representado como 14/10 e 1,3 poderia ser representado como 13/10. Na verdade, qualquer número que possua apenas um dígito após o ponto (ou a vígula) decimal poderia ser escrito como um inteiro sobre 10. Em uma linha de raciocínio semelhante, qualquer número com dois dígitos após o decimal poderia ser escrito como um inteiro sobre 100, como, por exemplo, 1,23 = 123/100. Podemos prosseguir com isso indefinidamente, até mesmo com um caso extremo como 1.23456789 = 123456789/100000000. Como nosso denominador (o número de baixo) pode ter o tamanho que desejarmos, pode parecer que TODO E QUALQUER número pode ser representado de tal maneira, sendo, portanto, racional. Entretanto, existem alguns números que são irracionais, como p. Você pode escrever quantos dígitos quiser para representar p, e alguém ainda poderia surgir depois e acrescentar outro dígito. Ele não se repete, até onde sabemos, mesmo que sejam computados milhões de dígitos. As funções verdadeiramente aleatórias são assim – elas efetivamente existem, mas só podemos lidar com elas do modo como lidamos com p, por aproximação. Poderíamos representar p como o número racional 3,14159. Não se trata do verdadeiro p, mas é suficientemente próximo para a maioria das aplicações. Da mesma forma, seria possível encontrar uma aproximação para um sinal verdadeiramente aleatório considerada suficientemente boa para a aplicação.

  1.9.2   Amostragem de Sinais Reais Suponha que você possua a seguinte sequência de números, com pouca informação a respeito deles: {59, 65, 68, 70, 61, 61, 63, 66, 66, 67, 70, 71, 71, 70, 70, 70}. Você poderia calcular a média e a variância e encontrar os valores mínimo e máximo. Se você tivesse que adivinhar o provável valor do sinal em um instante de tempo arbitrário, 66 aparentemente seria um bom palpite. Suponha agora que você recebeu a informação de que esses valores correspondem a leituras de temperatura em graus Fahrenheit. Esses números poderiam ser utilizados para decidir como se vestir de acordo com o clima, e você pode chegar à conclusão de que roupas de mangas compridas seriam suficientemente quentes, talvez com um suéter. Agora suponha que essas leituras tenham sido coletadas em Atlanta durante o mês de agosto. Como isso seria possível? Atlanta é conhecida por seu clima quente, especialmente em agosto. Existe uma informaçãochave que está faltando aqui: essas temperaturas foram coletadas todos os dias às duas da madrugada. Agora tudo faz sentido, pois as temperaturas foram lidas numa hora do dia em que provavelmente seriam as mais baixas. A coleta de amostras em uma frequência muito baixa resulta em conclusões equivocadas. Em nosso exemplo, alguém poderia presumir que as temperaturas flutuam em torno de apenas alguns graus. Ou alguém poderia presumir que as temperaturas são um indicativo do clima, e não um caso extremo. Esperamos que a temperatura se eleve à tarde e caia durante a noite, de forma semelhante a uma função senoidal. Mas esse exemplo mostra como podemos deixar passar a tendência global de elevação e queda coletando amostras em intervalos regulares que, por acaso, coincidem com os pontos baixos. Se aumentássemos um pouco a frequência de amostragem – digamos, a cada 23 horas, começando com a leitura inicial às 02h00, esse novo conjunto de leituras não seria melhor que o anterior. Eles passariam a impressão de que a temperatura eleva-se gradualmente (na medida em que as amostras passam a ser coletadas mais cedo: a primeira às 02h00, depois à 01h00, depois à meia-noite, depois às 23h00 etc., eventualmente atingindo os horários vespertinos) e depois decai gradualmente (quando as amostragens começam cada vez mais cedo, já pela manhã). Uma taxa de amostragem mais eficiente seria a cada 12 horas ou menos, pois teríamos uma ideia muito melhor do padrão de flutuação da temperatura. Na pior das hipóteses, coletaríamos temperaturas exatamente entre os extremos, mas ainda assim teríamos uma boa ideia sobre a média.

Weeks 001.indd 22

14/08/11 19:40

Introdução    23

  1.9.3   Tempo e Frequência Utilizamos tanto o tempo quanto a frequência em linguagem para visualizar a mesma informação de diferentes maneiras. Você pode, por exemplo, perguntar “por quanto tempo posso dirigir antes de precisar trocar o óleo do meu carro?” – nesse caso, a resposta seria em termos de tempo (meses). Uma pergunta correlata, “quantas vezes ao ano preciso trocar o óleo do meu carro?”, resulta em uma resposta baseada em frequência (mudanças de óleo por ano). Tempo e frequência possuem uma relação inversa em linguagem, assim como em DSP. Estamos habituados a pensar a respeito da frequência em alguns contextos. Quando você se muda para uma nova cidade, provavelmente instala seu aparelho de som e percorre os canais de rádio. Depois de mover o botão o máximo possível para a esquerda, você o retorna lentamente até ouvir alguma coisa. Se gosta do que ouve, registra a posição do botão e continua movendo-o para a direita. Ao terminar, você essencialmente terá criado um gráfico do espectro: nos pontos onde encontrou energia diferente de zero para a posição do botão, você marcou a frequência. Você pode imaginar por que as estações de rádio muitas vezes possuem números depois do ponto (ou da vírgula) decimal, tal como 88.5 em vez de apenas 88 ou 89. Esses números são expressos em MHz, ou milhões de Hertz, e o dígito extra ajuda a sintonizar um bom sinal. Depois que você se formar, conseguir um ótimo emprego e juntar US$ 1,3 milhão em sua conta bancária, lembre-se desse exemplo e de como 0,3 é importante para você! Você pode ter percebido que as estações de rádio FM terminam em números ímpares, ao menos nos Estados Unidos. Lá, o espectro FM foi alocado em blocos de 200 kHz, começando em um limite ímpar. Assim, você pode ter uma estação local em 88,5 MHz, mas não uma em 88,6 MHz. Agora imagine que você disponha de uma medida de toda a energia do espectro de rádio para um instante no tempo. Como você poderia utilizar essa informação para indicar a estação de rádio que gostaria de ouvir? Você não pode utilizá-la para essa finalidade. Embora esteja habituado aos sinais em termos de tempo, existem alguns casos em que a informação de frequência torna as coisas muito mais fáceis.

  1.10   Somas No Processamento Digital de Sinais, você provavelmente se deparará com fórmulas contendo somas e outras coisas aparentemente complicadas. Não há nada a temer em relação às somas. A fórmula e o código C/C++ a seguir, por exemplo, cumprem a mesma tarefa. Esse exemplo de soma constitui apenas uma forma compacta de se expressar uma ideia: para cada número em x, multiplique por dois e subtraia um, some todos os resultados e armazene a resposta final na variável s. Consideremos x = {1.4, 5.7, 3.0, 9.2, 6.8} e N o número de valores em x.

Eis uma parte de um programa em C/C++ que faz a mesma coisa: int i; float x[] = {1.4, 5.7, 3.0, 9.2, 6.8}; int N=5; // o número de valores em x[] float s = 0; for (i=0; i> a a = 3 >> b b = 3.1000 >> c c = 3.1000 + 5.2000i O MATLAB exibe o valor complexo armazenado em c com i representando a parte complexa (a raiz quadrada de −1). Note, contudo, que ele também entende j como a declaração de atribuição para exibições de c. Tanto i quanto j podem ser utilizados como variáveis, tal como em outras linguagens. Mas, para evitar confusão, é aconselhável utilizar k ou algum outro nome de variável. É uma boa ideia utilizar nomes de variáveis em letras maiúsculas para matrizes e nomes em letras minúsculas para escalares. Isso não é obrigatório, pois o MATLAB não se importa com tal convenção. Mas, por diferenciar maiúsculas e minúsculas, a variável x é considerada diferente da variável X. Uma observação final a respeito das funções é que as variáveis utilizadas em uma função não são acessíveis fora daquela função. Embora isso ajude o programador a manter variáveis locais, também significa que, se a função gerar um erro, o programador não será capaz de simplesmente digitar o nome de uma variável no prompt para descobrir seu valor. Uma solução rápida para isso é inserir um comentário na linha de declaração da função e chamá-la novamente depois. Como alternativa, o MATLAB provê pontos de interrupção (breakpoints) e outros recursos de interface de programação que se espera encontrar em um kit de desenvolvimento de software.

  2.2   Como Obter Ajuda e Escrever Comentários O comando help é bastante útil. Muitas vezes, um(a) programador(a) lembrará a função que deseja utilizar, mas precisa primeiramente verificar o nome e depois descobrir quais são os parâmetros e a ordem dos mesmos. O comando help é útil para esse propósito. Simplesmente digite help no prompt, seguido do nome da função – por exemplo, help conv fornece informações sobre a função de convolução (conv). Durante a criação de uma função ou programa, é importante documentar o código. Ou seja, certifique-se de incluir comentários suficientes para que outra pessoa possa entender o que o código faz. Essa “outra pessoa” pode ser até mesmo você, caso você crie um programa e só venha a utilizá-lo novamente muito tempo depois. Os comentários em MATLAB começam com um sinal de percentagem “%” e continuam até o final da linha, tal como o comentário estilo C++ (de duas barras inclinadas para a direita, //). Um comentário pode ser inserido na mesma linha que um comando, desde que o comando venha primeiro (caso contrário, ele será tratado como parte do comentário). É uma boa ideia inserir diversas linhas de comentários no topo de seus programas e funções. Inclua informações como a ordem das entradas, a ordem das saídas e uma breve descrição do que o código faz. Um aspecto conveniente do MATLAB é que os comentários no topo de seu arquivo retornarão por meio da função de ajuda (help). Faça esta experiência: digite help xyz no prompt do MATLAB. Ele deverá informá-lo de que tal função não existe.

Weeks 002.indd 28

06/03/12 09:20

MATLAB®    29

>> help xyz xyz.m not found. Digite a função abaixo e salve-a como “xyz.m”: % % Esta e minha funcao. Ela retorna a entrada como saida. % % Uso: a = xyz(b) % function [a] = xyz(b) a=b; Agora digite novamente help xyz. Você deverá visualizar as primeiras linhas na tela. Certifique-se de incluir o sinal de percentagem na coluna mais à esquerda caso deseje que esses comentários sejam encontrados pelo recurso de ajuda. >> help xyz Esta e minha funcao. Ela retorna a entrada como saida. Uso:

a = xyz(b)

Experimente também o comando docsearch, que provê uma interface gráfica.

  2.3   Conceitos Básicos de Programação MATLAB Esta seção abrange os pontos principais que você precisa conhecer para trabalhar com o MATLAB. O comando clc limpa a janela de comando e é semelhante ao comando clear no ambiente Unix. O comando clear, aliás, instrui o MATLAB a livrar-se de todas as variáveis ou pode ser empregado para descartar uma variável específica – por exemplo, clear A. A limpeza de uma variável libera memória, o que é bom quando as variáveis não são mais necessárias. Para ter acesso a uma lista das variáveis presentes na memória, digite who. O comando similar whos fornece a mesma lista, porém com mais informações. >> A = 3; >> who Your variables are: A >> whos Name A

Size

Bytes

1x1

8

Class double array

Grand total is 1 element using 8 bytes >> clear >> who >>

Weeks 002.indd 29

06/03/12 09:20

30    CAPÍTULO 2 No exemplo anterior, constatamos que a variável A é definida e vemos a diferença entre who e whos. Após a declaração clear, o comando who nada retorna, pois nos livramos das variáveis.

  2.3.1   Escalares, Vetores e Matrizes É possível atribuir um valor “normal” (escalar) a uma variável, tal como b=3 ou b=3.1, mas também é fácil atribuir a ela diversos valores de uma vez, tornando-a um vetor ou até mesmo uma matriz. Por exemplo, c=[1 2] armazena ambos os números na variável c. Para criar uma matriz, utilize um sinal de ponto e vírgula (“;”) para separar as linhas, como d=[1 2; 3 4; 5 6]. Isto faz de d uma matriz de três linhas e duas colunas. Para acessar um desses valores, utilize dois índices – por exemplo, d(2,2) retorna o valor 4. >> d=[1 2; 3 4; 5 6] d = 1 3 5

2 4 6

E se você quiser acrescentar outra linha em d? O procedimento é simples – basta atribuir-lhe um valor: d(4,:)=[7 8]. Note o sinal de dois pontos, “:”, na posição do especificador de coluna. Isso é intencional; um pequeno truque para especificar o intervalo inteiro. Poderíamos ter utilizado o comando d(4,1:2)=[7 8] para dizer a mesma coisa, mas a primeira versão é mais fácil (e requer menos digitação). >> d(4,:)=[7 8] d = 1 3 5 7

2 4 6 8

E se desejarmos selecionar somente a coluna 1? O código a seguir produz este resultado: >> d(:,1) ans = 1 3 5 7 Compare a saída anterior com o seguinte comando: >> d(1,:) ans = 1

2

Os dois últimos exemplos mostram como acessar a coluna 1 e a linha 1, respectivamente.

Weeks 002.indd 30

06/03/12 09:20

MATLAB®    31

  2.3.2   Intervalos de Números Uma variável pode conter um intervalo (ou faixa) de números. Imagine, por exemplo, que a variável N tivesse de conter todos os números 1, 2, 3, 4 etc. até o número 100. Eis um modo de criar N: for index = 1:100 N(index) = index; end Isso resolve o problema, mas existe uma forma mais rápida e mais fácil de fazer a mesma coisa. A seguir, atribuímos um intervalo a N, especificado com um sinal de dois pontos, “:”, entre os valores inicial e final. N = 1:100; Se um terceiro número for especificado, o segundo será utilizado como o incremento. Por exemplo, M=0:0.4:2 atribuiria a sequência {0, 0.4, 0.8, 1.2, 1.6, 2} à variável M. O exemplo a seguir exibe todos os números entre 8 e 20, com um incremento de 2. >> 8:2:20 ans = 8

10

12

14

16

18

20

Também podemos utilizar incrementos negativos (decrementos), conforme mostra o exemplo abaixo. >> 20:-2:8 ans = 20

18

16

14

12

10

8

  2.3.3   Saída Muitas vezes, durante sua execução, um programa precisa transmitir informações para o usuário. Para exibir uma sequência ou um número na tela, o comando sprintf é rápido e simples. >> sprintf(O valor de pi e %5.3f, pi) ans = O valor de pi e 3.142 Note que não há ponto e vírgula ao final do comando. A razão para tal é que isso suprimiria a saída, ao passo que nosso desejo é que a saída seja exibida na tela do usuário. Naturalmente, sprintf também pode ser utilizado para criar uma sequência a ser armazenada em uma variável. Como em outras linguagens de programação, sempre há mais de uma maneira de se fazer a mesma coisa. O comando disp também pode exibir informações na tela, não importando se for seguido por um sinal de ponto e vírgula. Ele pode ser utilizado em conjunto com sprintf, tal como mostra o exemplo a seguir. >> disp(isso e uma string) isso e uma string

Weeks 002.indd 31

06/03/12 09:20

32    CAPÍTULO 2 >> disp(isso e uma string); isso e uma string >> disp(sprintf(O valor de pi e %5.3f,pi)) O valor de pi e 3.142 Os apóstrofos especificam uma sequência de caracteres (string). Observe que a mudança na saída depende da presença ou não dos apóstrofos. >> hello = 5; >> disp(hello) hello >> disp(hello) 5

  2.3.4   Declarações Condicionais (if) Tal como em outras linguagens de programação, a declaração if corresponde à forma de execução condicional de um código. No exemplo a seguir, a divisão só é feita quando o denominador não é igual a zero. A divisão por zero é um erro comum que pode interromper um programa. % Proteger contra divisao por 0 if (denominator ˜= 0) result = numerator / denominator; end Eis outra forma de se lidar com isso: no próximo exemplo, a declaração if ou executa o sprintf ou executa a divisão, mas não ambos. Note como um único sinal de igual indicaria uma atribuição – por exemplo, a=1 –, mas quando se comparam dois valores, dois sinais de igual são utilizados – por exemplo, if (a==1). Dessa forma, fica claro para o computador qual operação executar (atribuição ou comparação). Infelizmente, a confusão entre um e outro constitui uma fonte de erros comum em um programa. % Proteger contra divisao por 0 if (denominator == 0) sprintf(Não e possivel efetuar a divisao pois denominador e 0) else result = numerator / denominator; end Como era de se esperar, é possível aninhar declarações if. O código a seguir, por exemplo, verifica um par de índices (m e n) antes de permitir que eles sejam utilizados para acessar um valor de uma matriz. Esse tipo de verificação é útil para garantir que o programa não trave (crash). Antes que esse código seja executado, contudo, os valores para m, n e A (a matriz) precisam ser definidos. A = [1 2; 3 4; 5 6; 7 8]; m=0; n=1; Agora experimente o código a seguir. Observe que if and(m>0, n>0) seria equivalente a if ((m> 0) && (n>0)) em C/C++. Tente diferentes valores para m e n. Um modo fácil de tentar isso repetidamente é utilizar o editor embutido do MATLAB e salvar esses comandos em um arquivo (por exemplo, “checkindices.m”). Quando desejar executá-lo, simplesmente digite o nome do arquivo sem a extensão (em nosso exemplo, checkindices) no prompt de comando. O código não tem uma precondição, já que espera que A, m e n sejam definidos antes que ele seja executado.

Weeks 002.indd 32

06/03/12 09:20

MATLAB®    33

% Verifique se os indices m e n sao validos [rows, cols] = size(A); if and(m>0, n>0) if and(m0) no código anterior. Eis outro breve exemplo: >> array = [37 85 59]; >> index = 3; >> if (index >=1 ) && (index > array array = 37

85

61

A declaração else acompanha o if mais próximo. Note que else if é tratado de forma diferente de elseif. O comando else if é tratado como duas declarações separadas; um end é esperado tanto para o segundo if quanto para o primeiro. O comando elseif é uma combinação do primeiro if, de forma muito semelhante a uma declaração switch/case. Os dois exemplos a seguir mostram como a declaração if pode ser estruturada. % Uma forma de estruturar a declaracao if if (a == 1) sprintf(a = 1) else if (a == 2) sprintf(a = 2) end end % Outra forma de estruturar a declaracao if if (a == 1) sprintf(a = 1) elseif (a == 2) sprintf(a = 2) end Qualquer if deve possuir um end correspondente, a menos que ele seja um elseif. Por uma questão de clareza, é melhor utilizar elseif ou alocar else e if em linhas diferentes.

  2.3.5   Loops A repetição é algo comum em programas, e o MATLAB disponibiliza os loops (laços) for e while para essa finalidade. A declaração end marca de forma natural o fim do loop.

Weeks 002.indd 33

06/03/12 09:20

34    CAPÍTULO 2 count = 0; while (count < 10) B(count+1) = count*2; count = count+1; end Como alternativa, o código a seguir – um exemplo de um loop for – faz a mesma coisa. for count=0:9 B(count+1) = count*2; end O loop for repete-se um número programado de vezes. O loop while irá se repetir até que atinja a condição de conclusão. Tome cuidado para que seu loop while não se repita indefinidamente. Uma observação final é que o MATLAB é capaz de processar operações de lista* de forma eficiente. Frequentemente, loops como esses podem ser eliminados, conforme mostra o exemplo abaixo. count=0:9; B(count+1) = count*2;

  2.3.6   Continuação de uma Linha Nos exemplos de MATLAB neste livro, eventualmente você encontrará reticências (“...”) ao final de uma linha. Elas simplesmente informam ao MATLAB que o comando prossegue na linha seguinte – uma necessidade quando se emprega extensões máximas de colunas em uma impressão.

  2.4   Exemplos Aritméticos O termo vetor tem dois significados neste livro, dependendo do contexto. No primeiro, a palavra “vetor” é empregada como um sinônimo de ‘lista’ (array). No segundo, ela também pode significar uma magnitude e um ângulo formando um conjunto de coordenadas polares (também chamado de “fasor” – phasor –, especialmente se o ângulo mudar, ou seja, se ele girar como o ponteiro de segundos de um relógio, mesmo que geralmente gire no sentido anti-horário). O respectivo significado deve ficar aparente no texto. Tratando-se de matrizes, a ordem é importante. Suponha, por exemplo, que você possua as matrizes A, B e C. Se elas forem multiplicadas entre si, (AB)C não será o mesmo que A(BC). Felizmente, o MATLAB se encarrega dos detalhes dos cálculos envolvendo matrizes e vetores complexos. Eis alguns exemplos de operações aritméticas comuns em MATLAB. Primeiro, definiremos algumas variáveis com as quais trabalharemos. >> A = [20 1 9 16] A = 20

1

9

16

>> A2 = [20, 1, 9, 16]

% As entradas podem ser separadas por virgulas

A2 = 20

1

9

16

* Também conhecidas como “operações de array”. (N.T.)

Weeks 002.indd 34

06/03/12 09:20

MATLAB®    35

Como vimos no código anterior, a inclusão de vírgulas após cada elemento não só funciona como também torna a separação mais clara. >> B = [ 5 4 3 2] B = 5

4

3

2

>> C = [ 0 1 2 3; 4 5 6 7; 8 9 10 11] C = 0 4 8

1 5 9

2 6 10

3 7 11

>> D = [ 8; 12; 0; 6]

% As linhas sao separadas por sinais de ponto e virgula

D = 8 12 0 6 >> F = [ 31 30 29 28; 27 26 25 24; 23 22 21 20] F = 31 27 23

30 26 22

29 25 21

28 24 20

Sinais de ponto e vírgula podem ser empregados para separar as linhas de uma matriz, conforme vimos no último exemplo de código, em que todas as operações são declarações de atribuição. A maior parte dos exemplos abaixo não é composta de declarações de atribuição; isto é, os resultados apenas são exibidos na tela e não afetam as matrizes anteriores. O comando size retorna o número de linhas e o número de colunas para uma dada matriz. Vemos aqui que A e B são ambas listas, matrizes com apenas uma linha. C, por sua vez, é uma matriz de três linhas e quatro colunas. D também é uma lista, pois possui apenas uma coluna. size(A) ans = 1

4

>> size(C) ans = 3

4

>> size(D) ans = 4

Weeks 002.indd 35

1

06/03/12 09:20

36    CAPÍTULO 2 Uma variável seguida de um ponto e um apóstrofo indica a transposição daquela variável. A notação A. representa simplesmente a transposição da matriz A. Podemos obter o mesmo efeito utilizando transpose(A) em seu lugar. O exemplo a seguir mostra a transposição de A, ou seja, passa a visualizá-la como uma matriz 4 × 1 em vez de uma matriz 1 × 4. Podemos verificar isso com a função size. Em uma matriz como a C, o resultado é a troca das linhas pelas colunas. Da mesma forma, vemos que C é uma matriz 3 × 4, enquanto a transposição de C é 4 × 3. >> A.{

% transposicao de A

ans = 20 1 9 16 >> size(A.) ans = 4 >> C.{

1 % transposicao de C

ans = 0 1 2 3

4 5 6 7

8 9 10 11

>> size(C.) ans = 4

3

Existe uma operação similar para a transposição com conjugado complexo. Um conjugado complexo significa simplesmente que mudamos o sinal na parte imaginária do número. Compare as duas operações a seguir. >> Y = [1-2j, 2+3j]; >> disp(Y.) 1.0000 - 2.0000i 2.0000 + 3.0000i >> disp(Y) 1.0000 + 2.0000i 2.0000 - 3.0000i Vemos que a primeira operação transpôs a lista Y conforme o esperado. A segunda operação trocou os sinais das partes imaginárias, produzindo a transposição do conjugado complexo de Y. Se a lista (ou matriz) fosse real, as duas operações teriam retornado o mesmo resultado.

Weeks 002.indd 36

06/03/12 09:20

MATLAB®    37

A adição e a subtração podem ser efetuadas conforme o esperado. Conforme vemos abaixo, uma constante pode ser adicionada a cada elemento de uma matriz, por exemplo, A+7. Podemos somar cada elemento de duas matrizes, como em A+B. A subtração é efetuada facilmente com A–B e B–A. >> A+7 ans = 27

8

>> A+B

16

23

% Note que A e B devem ser do mesmo tamanho.

ans = 25

5

12

18

-3

6

14

3

-6

-14

>> A-B ans = 15 >> B-A ans = -15

Para encontrar a soma de uma matriz, utilize a função sum. Ela também funciona com matrizes bidimensionais, embora some todos os números nas colunas e retorne uma lista. Observe, logo a seguir, que existem duas formas de se expressar a mesma coisa. O comando sum(A+B) executa a soma da matriz A com a matriz B e depois encontra a soma do resultado. A segunda forma de se fazer isso, sum(A) + sum(B) encontra a soma de A e a soma de B, adicionando em seguida o resultado das duas somas. Como em qualquer linguagem de programação, existem várias maneiras de se fazer a mesma coisa. Tal aspecto se torna importante quando o desempenho é deficiente. Talvez seja preciso especificar novamente as operações de uma forma mais eficiente. >> sum(A) ans = 46 >> sum(B) ans = 14 >> % Adicione A a B e descubra sua soma em seguida >> sum(A+B) ans = 60

Weeks 002.indd 37

06/03/12 09:20

38    CAPÍTULO 2 >> % encontre a soma de A e a soma de B e depois adicione >> sum(A) + sum(B) ans = 60 Os exemplos a seguir referem-se às operações de multiplicação. A multiplicação pode ser um pouco mais complicada, por requerer diversas operações similares. Vamos começar tentando multiplicar A por B. >> A*B ??? Error using ==> * Inner matrix dimensions must agree. Não podemos multiplicar uma matriz 4 × 1 por uma matriz 4 × 1! Se quisermos multiplicar duas matrizes entre si, precisaremos nos certificar de que o número de colunas da primeira matriz corresponde ao número de linhas da segunda matriz. Nesse caso, devemos transpor A ou B de modo a multiplicar uma pela outra. Observe que, ao multiplicarmos A pela transposição de B, estamos multiplicando os enésimos elementos entre si e somando os resultados. >> A.*B

% Multiplique transposicao de (A) por B

ans = 100 5 45 80 >> A*B.

80 4 36 64

60 3 27 48

40 2 18 32

% Multiplique A por transposicao de (B).

ans = 163 >> A(1)*B(1) + A(2)*B(2) + A(3)*B(3) + A(4)*B(4) ans = 163 E se quisermos multiplicar os enésimos elementos entre si, mas sem somar os resultados? Isso pode ser conseguido incluindo-se um ponto antes do sinal de multiplicação, para informar que desejamos multiplicar os elementos individuais entre si. Observe que agora não precisamos transpor nenhuma lista. >> A.*B

% ans = [A(1)*B(1)

A(2)*B(2)

A(3)*B(3)

A(4)*B(4)]

ans = 100

4

27

32

Podemos multiplicar uma lista por um valor escalar, conforme visto no exemplo a seguir. Além disso, podemos tomar os resultados de tal operação e somá-los com a função sum. >> A*4

% multiplicar todos os valores de A por 4

ans = 80

Weeks 002.indd 38

4

36

64

06/03/12 09:20

MATLAB®    39

>> sum(A*4) ans = 184 Quando se trata de multiplicar matrizes por listas, os resultados são os que se poderia esperar utilizando-se álgebra linear. >> [1 2 3]*C % Multiplique o vetor linha [1 2 3] por C % resultado(1) = 0*1 + 4*2 + 8*3 % resultado(2) = 1*1 + 5*2 + 9*3 % resultado(3) = 2*1 + 6*2 + 10*3 % resultado(4) = 3*1 + 7*2 + 11*3 % Os resultados estao todos em uma linha. ans = 32

38

44

50

>> C*[1 2 3 4]. % Multiplique C por [1 2 3 4] como um vetor coluna % resultado(1) = 0*1 + 1*2 + 2*3 + 3*4 % resultado(2) = 4*1 + 5*2 + 6*3 + 7*4 % resultado(3) = 8*1 + 9*2 + 10*3 + 11*4 % Os resultados estao todos em uma coluna. ans = 20 60 100 Existem diversas maneiras de se multiplicar F e C. Tentar efetuar F*C não funciona, pois as dimensões não estão corretas. Se F ou C for transposto, não haverá problema. Os números dessas duas matrizes aparecem multiplicados e somados na variável result, mostrando que o resultado da multiplicação é aquele que deveríamos esperar. Utilizamos reticências após result para prosseguirmos na linha seguinte, apenas para ajustar os resultados nas linhas. >> F.*C

% transposicao de (F) * C

ans = 292 280 268 256

373 358 343 328

454 436 418 400

>> result = ... [31*0+27*4+23*8, 30*0+26*4+22*8, 29*0+25*4+21*8, 28*0+24*4+20*8,

535 514 493 472

31*1+27*5+23*9, 30*1+26*5+22*9, 29*1+25*5+21*9, 28*1+24*5+20*9,

31*2+27*6+23*10, 30*2+26*6+22*10, 29*2+25*6+21*10, 28*2+24*6+20*10,

31*3+27*7+23*11; 30*3+26*7+22*11; 29*3+25*7+21*11; 28*3+24*7+20*11]

result = 292

Weeks 002.indd 39

373

454

535

06/03/12 09:20

40    CAPÍTULO 2 280 268 256

358 343 328

436 418 400

514 493 472

>> F*C. ans = 172 148 124

644 556 468

1116 964 812

>> result = ... [31*0+30*1+29*2+28*3, 31*4+30*5+29*6+28*7, 31*8+30*9+29*10+28*11; 27*0+26*1+25*2+24*3, 27*4+26*5+25*6+24*7, 27*8+26*9+25*10+24*11; 23*0+22*1+21*2+20*3, 23*4+22*5+21*6+20*7, 23*8+22*9+21*10+20*11] result = 172 148 124

644 556 468

1116 964 812

A título de exercício, verifique os cálculos exibidos na definição da matriz result. Podemos alterar a ordem das matrizes e descobrir que obtemos o mesmo resultado que o anterior, só que transposto. Um modo fácil de verificar isso consiste em subtrair um resultado (transposto) do outro, para constatar se todos os resultados das subtrações são iguais a zero. >> C.*F

% fornece a mesma resposta que F.*C, so que transposta.

ans = 292 373 454 535

280 358 436 514

268 343 418 493

256 328 400 472

>> (F.*C). - C.*F ans = 0 0 0 0

0 0 0 0

>> C*F.

0 0 0 0

0 0 0 0

% fornece a mesma resposta que F*C., so que transposta.

ans = 172 644 1116

148 556 964

124 468 812

>> (F*C.). - C*F.

Weeks 002.indd 40

06/03/12 09:20

MATLAB®    41

ans = 0 0 0

0 0 0

0 0 0

Suponha que desejemos multiplicar cada elemento de C pelo elemento correspondente de F. Podemos fazer isso com o operador “.*”, tal como fizemos antes. A propósito, a reversão da ordem dos operandos produz o mesmo resultado. >> C.*F ans = 0 108 184

30 130 198

58 150 210

84 168 220

>> result = [0*31, 1*30, 2*29, 3*28; 4*27, 5*26, 6*25, 7*24; 8*23, 9*22, 10*21, 11*20] result = 0 108 184

30 130 198

58 150 210

84 168 220

Tal como a multiplicação, a divisão possui algumas interpretações diferentes, dependendo de “./” ou “/” ter sidoespecificado. Por exemplo, A./B especifica que elementos de A devem ser divididos por elementos correspondentes de B. >> A = [20 1 9 16]; >> B = [5 4 3 2]; >> A./B ans = 4.0000

0.2500

3.0000

8.0000

>> result = [ 20/5, 1/4, 9/3, 16/2 ] result = 4.0000

0.2500

3.0000

8.0000

Podemos especificar que elementos de B devem ser divididos pelos elementos correspondentes de A, de duas maneiras diferentes: utilizando-se a barra invertida A.\B ou a barra inclinada A./B. Ao nos referirmos aos valores de result do exemplo anterior, podemos calcular o inverso de cada elemento (ou seja, 1 sobre cada valor). >> A.\B ans = 0.2500

Weeks 002.indd 41

4.0000

0.3333

0.1250

06/03/12 09:20

42    CAPÍTULO 2 >> B./A ans = 0.2500

4.0000

0.3333

0.1250

4.0000

0.3333

0.1250

>> 1./result ans = 0.2500

Também podemos dividir cada elemento da matriz (C) por um valor escalar (5). >> C/5 ans = 0 0.8000 1.6000

0.2000 1.0000 1.8000

0.4000 1.2000 2.0000

0.6000 1.4000 2.2000

Em seguida, dividimos os elementos da matriz C pelos elementos correspondentes de F. O primeiro resultado é 0/31, ou 0. Se permutássemos as duas matrizes, isto é, utilizando os elementos de F como numeradores (a parte superior das frações) enquanto os elementos de C são utilizados como denominadores (a parte inferior das frações), a operação ainda seria possível, mas a divisão por zero faria com que o primeiro resultado fosse Inf (infinito). >> C./F ans = 0 0.1481 0.3478

0.0333 0.1923 0.4091

0.0690 0.2400 0.4762

0.1071 0.2917 0.5500

>> result = [ 0/31, 1/30, 2/29, 3/28; 4/27, 5/26, 6/25, 7/24; 8/23, 9/22, 10/21, 11/20] result = 0 0.1481 0.3478

0.0333 0.1923 0.4091

0.0690 0.2400 0.4762

0.1071 0.2917 0.5500

Por fim, podemos dividir uma matriz por outra. Eis um exemplo que utiliza valores “convenientes” para a segunda matriz. A função inv retorna o inverso da matriz. Espera-se que o resultado da divisão da matriz seja o mesmo que o da multiplicação da primeira matriz pelo inverso da segunda. Em outras palavras, M/N = M × N −1, tal como uma operação com valores escalares. O código abaixo verifica se nossos resultados são aqueles que esperamos. M = [1 2 3; 4 5 6; 7 8 9] M = 1 4 7

Weeks 002.indd 42

2 5 8

3 6 9

06/03/12 09:20

MATLAB®    43

>> N = [1 2 0; 3 0 4; 0 5 6] N = 1 3 0

2 0 5

0 4 6

>> inv(N) ans = 0.3571 0.3214 -0.2679

0.2143 -0.1071 0.0893

>> M/N

-0.1429 0.0714 0.1071

% Isso e o mesmo que M*inv(N)

ans = 0.1964 1.4286 2.6607

0.2679 0.8571 1.4464

0.3214 0.4286 0.5357

0.2679 0.8571 1.4464

0.3214 0.4286 0.5357

>> M*inv(N) ans = 0.1964 1.4286 2.6607

Existem muitas outras operações em MATLAB. Para ter acesso a uma lista delas, digite help ., ou seja, a palavra help seguida de um espaço em branco e um ponto.

  2.5   Funções O MATLAB suporta funções. Para ter maior flexibilidade, armazene as funções separadamente, com o mesmo nome de arquivo que o nome da função. Em MATLAB, as variáveis locais de uma função são removidas após a execução da mesma. Isso pode ser frustrante caso a função precise ser depurada. Uma vez que o programa esteja funcionando bem, é uma boa ideia encapsulá-lo em uma função. A função a seguir, por exemplo, toma dois valores escalares como argumentos (x e y) e retorna dois valores (r e teta). Sejam quais forem os valores de r e teta ao final da função, eles serão fornecidos. Essa função deve ser salva como “convert–to–polar.m” (“converter em polar”) no espaço de trabalho local (o padrão). % % Comentarios % function [r, theta] = convert_to_polar(x, y) %... alocar codigo adicional aqui

Weeks 002.indd 43

06/03/12 09:20

44    CAPÍTULO 2 Ela seria executada pelo comando a seguir. Os nomes dos parâmetros não precisam ser os mesmos que aqueles presentes na definição da função. >> [vector_length, vector_angle] = convert_to_polar(a, b); A versão integral dessa função pode ser encontrada no site da LTC Editora.

  2.6   Como NÃO Traçar uma Senoide Funções senoidais são importantes para o processamento de sinal. Suponha que desejemos criar um gráfico de uma senoide de 200 Hz. Isso parece ser bem simples, mas pode apresentar diversas dificuldades, conforme esta seção demonstra. Já tínhamos uma expressão para senoides, a × cos(2pft + f). Se quiséssemos, poderíamos utilizar a função seno em seu lugar, mas essa expressão servirá. Sabemos qual é a frequência e podemos uti­li­zar a = 1 e f = 0 para simplificar. O plano é gerar uma lista dos valores de cosseno e traçar a lista em seguida. Uma primeira tentativa provavelmente seria algo como: for t=1:100 x = cos(2*pi*200*t); end plot(x); Esse código, contudo, resulta em desapontamento, pois o gráfico aparece em branco. Na verdade, o gráfico contém um único ponto em (1,1) – muito longe de nossa desejada senoide. Uma rápida verificação da variável x revela que se trata de um valor escalar. Precisamos especificar que x seja armazenado como uma lista pelo MATLAB. Tentamos novamente, utilizando t como um índice. for t=1:100 x(t) = cos(2*pi*200*t); end plot(x); Esse procedimento nos aproxima um pouco mais do objetivo, pois agora podemos ver uma linha reta traçada no gráfico. Mas por que vemos uma linha reta? Após uma inspeção, notamos que todos os valores de x são iguais a 1. Alguns exemplos em uma calculadora revelam que os valores para x estão efetivamente corretos. O problema aqui é o argumento para a função cosseno; 2p corresponde a uma rotação em torno do círculo. Assim, sempre podemos acrescentar 2p ao argumento de uma senoide sem alterar o resultado, ou seja: cos(0) = cos(2p) = cos(2p × 2). Visto que t sempre possui valores inteiros no código anterior, o argumento da função cosseno sempre é simplesmente um múltiplo de 2p daquele antes dele. Podemos corrigir isso introduzindo um incremento fracionário para o loop for. Note como alteramos também o limite superior em t, de modo a manter o mesmo número de pontos. for t=1:0.01:2 x(t) = cos(2*pi*200*t); end plot(x); Esse código acarreta um erro:

Weeks 002.indd 44

06/03/12 09:20

MATLAB®    45

??? Subscript indices must either be real positive integers or logicals. >> t t = 1.0100 Vemos que t possui um valor 1.01 e, se utilizarmos 1.01 para indexar uma lista, teremos um problema em praticamente qualquer linguagem computacional. Em outras palavras, não podemos utilizar x(t) a menos que t possua um valor inteiro. Para corrigir isso, podemos introduzir uma nova variável n para atuar apenas como um índice para a lista x. n = 1; for t=1:0.01:2 x(n) = cos(2*pi*200*t); n = n + 1; end plot(x); Esse código funciona bem, mas ainda traça uma linha reta. Novamente o problema está no argumento para a função cos. Tivemos a ideia correta – utilizar valores fracionários no argumento –, mas não avançamos o suficiente com ela. Quando multiplicamos 200 por t, geramos valores de 200 a 400, com incremento 2. Permanece o problema de utilizarmos múltiplos inteiros de 2p para os argumentos do cosseno. Para tentar corrigir isso, podemos diminuir o valor do incremento, conforme mostrado abaixo. Reduziremos o limite superior, de modo a manter o tamanho de nossa lista. n = 1; for t=1:0.001:1.1 x(n) = cos(2*pi*200*t); n = n + 1; end plot(x); Por fim dispomos de um código funcional que produz algo que lembra uma senoide. Para torná-la mais suave, podemos alterar novamente o incremento e o limite superior. n = 1; for t=1:0.0001:1.01 x(n) = cos(2*pi*200*t); n = n + 1; end plot(x); Agora temos uma senoide de aparência suave. Mas o eixo x contém os índices, e não os efetivos valores de t. Para retificarmos tal condição, modificamos a função plot de modo a especificar os valores do eixo x: plot(1:0.0001:1.01, x); Ainda não terminamos. O código funciona, mas poderia ser mais eficiente e compacto. De fato, nem mesmo precisamos do loop for com o MATLAB. Em vez disso, podemos expressar t como um intervalo, utilizá-lo como parte da expressão para x e depois novamente no comando plot. t = 1:0.0001:1.01; x = cos(2*pi*200*t); plot(t, x);

Weeks 002.indd 45

06/03/12 09:20

46    CAPÍTULO 2 Note como MATLAB armazena x como uma lista. Aqui, a diferença principal é que o argumento para a função cosseno contém um intervalo. Quando multiplicamos um intervalo por uma constante (ou três constantes, nesse caso), ainda assim terminamos com um intervalo. O MATLAB permite que a função cosseno funcione tanto com listas quanto com valores escalares e retorna uma lista. Na verdade, podería­mos até combinar as declarações anteriores: plot((1:0.0001:1.01), cos(2*pi*200*(1:0.0001:1.01))); Uma compactação desse nível prejudica a legibilidade do código. Portanto, o exemplo de três linhas acima constitui uma melhor solução, embora esteja faltando uma coisa importante: comentários. Outros aprimoramentos que podem ser desejáveis são um título e rótulos para os eixos x e y. Concluiremos essa discussão com uma solução aprimorada (a seguir). Também incluímos um comando axis que especifica a parte do gráfico que desejamos visualizar, ou seja, o eixo x de 1 a 1.01 e o eixo y de −1 a 1. O resultado final é exibido na Figura 2.1. % considere t um pequeno intervalo de tempo t = 1:0.0001:1.01; % encontre uma senoide de 200 Hz baseada em t x = cos(2*pi*200*t); plot(t, x); title(Uma senoide de 200 Hz); xlabel(Tempo); ylabel(Amplitude); axis([1 1.01 -1 1]);

Uma senoide de 200 Hz

1 0.8 0.6 0.4

Amplitude

0.2 0 – 0.2 – 0.4 – 0.6 – 0.8 –1 1

1.001

1.002

1.003

1.004

1.005 1.006 Tempo (seg.)

1.007

1.008

1.009

1.01

Figura 2.1  Uma senoide de 200 Hz produzida pelo código MATLAB do exemplo.

Weeks 002.indd 46

06/03/12 09:20

MATLAB®    47

  2.7   Traçado de uma Senoide O programa a seguir traça uma senoide a partir dos parâmetros fornecidos: frequência, magnitude e ângulo de fase. Ele também funciona com mais de uma senoide de cada vez. % % Fornecidas as frequencias, magnitudes e fases, % trace um conjunto de senoides (ate 4) (baseadas em plotharmonic.m) % % uso: plotsinusoids(freq, mag, phase) % function plotsinusoids(freq, mag, phase) max_plots = 4; num_points = 200;

% Numero maximo de senoides para tracar de uma vez % Numero de pontos por repeticao

% Verificar parametros if (or((length(mag)˜=length(freq)), ... (length(phase)˜=length(freq)))) error(Erro – freq., mag. e fase devem ser do mesmo tamanho) end % Precisamos disso para 2 repeticoes e num_points por repeticao. freq_min = min(freq); step = 2/(freq_min*num_points); t = 0:step:2*(1/freq_min); i=1; % i corresponde a nossa contagem de senoides % Determine quantas senoides tracar. % Se o usuario passar listas maiores do que 4, ignoraremos os ultimos elementos. my_limit = min(length(freq), max_plots); % Para cada senoide, trace while (i > gcd(25, 33) ans = 1 Isso significa que o sinal cos(2p25t) + cos(2p33t) se repete a cada segundo, ou 1/gcd(25,33). A Figura 2.3 demonstra isso. O sinal é traçado de 0 a 1 segundo na parte superior e de 1 a 2 segundos na parte inferior. Embora o sinal apresente seções de aparência semelhante, um exame cuidadoso revela que o sinal efetivamente não se repente até que 1 segundo tenha transcorrido. E se desejarmos uma visualização ampliada das duas senoides? O código a seguir faz justamente isso. Ele traça duas senoides e mostra uma seção de cada vez. A função pause proporciona ao usuário a chance de visualizar o gráfico antes que um novo seja traçado. Com isso, podemos constatar que as duas senoides começam no mesmo valor, mas não coincidem nesse mesmo valor a não ser no final. A Figura 2.4 exibe a saída desse programa para a parte final das senoides.

Weeks 002.indd 48

06/03/12 09:20

MATLAB®    49

Uma senoide de 25 Hz  uma senoide de 33 Hz

2

Amplitude

1 0 –1 –2

0

0.1

0.2

0.3

0.4

0.5 0.6 Tempo (seg.)

0.7

0.8

0.9

1.1

1.2

1.3

1.4

1.5 1.6 Tempo (seg.)

1.7

1.8

1.9

2

Amplitude

1 0 –1 –2

0

2

Figura 2.3  Esse sinal repete-se a cada segundo.

Uma senoide de 25 Hz (.) e uma senoide de 33 Hz (sólida)

1 0.8 0.6 0.4

Amplitude

0.2 0 – 0.2 – 0.4 – 0.6 – 0.8 –1 0.9

0.91

0.92

0.93

0.94

0.95 0.96 Tempo (seg.)

0.97

0.98

0.99

Figura 2.4  Uma visão ampliada de duas senoide de 0.9 a 1 segundo.

Weeks 002.indd 49

06/03/12 09:20

50    CAPÍTULO 2 % % % %

show_sins.m Mostre 2 senoides, 100 amostras por vez.

% Fique a vontade para alterar qualquer um dos valores padrao abaixo show_num = 100; % O numero de amostras a exibir por vez count = 100; % O numero de amostras a avançar por vez t = 0:0.001:1; % Nossa variavel de tempo simulada freq1 = 25; % frequencia1 freq2 = 33; % frequencia2 x1 = cos(2*pi*freq1*t); x2 = cos(2*pi*freq2*t); mytitle = ... sprintf(Uma senoide de %d Hz (.) e uma de %d Hz (solida),... freq1, freq2); start = 1; last = show_num; done = 0; while (done ˜=1) plot(t(start:last), x1(start:last), k.-, t(start:last), ... x2(start:last), g) axis tight; % Faça o grafico ter bom aspecto xlabel(Time (sec)); ylabel(Amplitude); title(mytitle); pause(1);

end

% Atualize offsets da lista last = last + count; start = start + count; if (start >= length(x1)) done = 1; end if (last > length(x1)) last = length(x1); end

  2.9   Cálculo de Erro Encontrar a diferença entre dois sinais constitui uma operação comum. Um sinal pode, por exemplo, ser comprimido e descomprimido em seguida, utilizando-se um algoritmo de compressão “com perdas”(lossy) (onde certo grau de erro no sinal é considerado aceitável). A quantidade de erros entre o original e as versões reconstruídas do sinal pode ser encontrada somando-se os valores absolutos das diferenças. A função de valor absoluto, abs, é realmente necessária? Considere os dois sinais a seguir, x e y: >> x = [1, 2, 5, 0, -2]; >> y = [-1, 4, 0, 5, -2]; >> sum(x-y) ans = 0

Weeks 002.indd 50

06/03/12 09:20

MATLAB®    51

Percebe-se claramente que x e y não são iguais, mas, como as diferenças positivas e as diferenças negativas cancelam uma a outra, esse método simples faz com que elas pareçam ser iguais. O erro entre dois sinais, x e y, pode ser encontrado com o comando a seguir. Observe que x e y devem ter a mesma extensão. >>

error = sum(abs(x - y))

error = 14 Outra forma de se medir o erro é conhecida como RMSE (root mean squace error ou raiz quadrada do erro médio quadrático), que é calculada segundo o código abaixo. Primeiramente, diff encontra a diferença entre os sinais x e y. Em seguida, encontramos sigma–squared (sigma ao quadrado), também conhecido como s2 ou variância, calculado elevando-se ao quadrado cada elemento em diff e somando-se os resultados. Por fim, podemos computar a RMSE. diff = x - y; sigma_squared = sum(diff.*diff); rmse = sqrt(sigma_squared/length(x)) Poderíamos ter utilizado o comando sigma_squared = diff*diff.; em vez da segunda linha, pois ele retorna a mesma resposta (e a multiplicação de matriz se encarrega da soma).

  2.10   Às Vezes 0 Não É Exatamente 0 Em programação, uma condição comum em uma declaração if ou while é a comparação de alguma variável com zero. Ao lidar com números de ponto flutuante, contudo, o computador fará exatamente o que mandarmos e concluirá que 0.000000000000000000000000000000000000000001 não é igual a zero. A declaração de atribuição a seguir mostra que esse mesmo exemplo será armazenado como um número diferente de zero. >> a = 0.000000000000000000000000000000000000000001 a = 1.0000e-42 Uma diferença tão pequena como essa é sempre intencional? O exemplo a seguir mostra que pode ocorrer um pequeno grau de erro. >> % mynumber deve ser 0.0 apos a proxima declaracao mynumber = sqrt(7)^2 - 7; % Aqui nos confirmamos que mynumber e zero, certo? if (mynumber == 0.0) sprintf(mynumber e zero)

Weeks 002.indd 51

06/03/12 09:20

52    CAPÍTULO 2 else sprintf(mynumber nao e zero) end ans = mynumber nao e zero >> O que aconteceu? Se mynumber não é zero, então o que é? >> mynumber mynumber = 8.8818e-016 O número é muito, muito pequeno, mas não absolutamente zero. Ele deveria ser zero, mas devido às realidades da precisão ocorre um pequeno erro.

  2.10.1   Comparação de Números com uma Tolerância Quando um valor é muito próximo de zero, mas tecnicamente diferente de zero, podemos compará-lo com certa tolerância, conforme mostra o exemplo a seguir. >> % mynumber deve ser 0.0 apos a proxima declaracao mynumber = sqrt(7)^2 - 7; % Estabeleca um limite de tolerancia tolerance = 0.000001; % Como ja sabemos que o valor de mynumber e muito pequeno, % mas nao exatamente 0, utilizaremos a comparacao a seguir. if and((mynumber < tolerance), (mynumber > -tolerance)) sprintf(mynumber e zero) else sprintf(mynumber nao e zero) end ans = mynumber e zero >> Com a tolerância incluída como um fator, a comparação nos fornece os resultados que esperamos. Essa é a única maneira de fazer a comparação, e o exemplo a seguir mostra a mesma ideia, apenas com a inclusão da função de valor absoluto abs para tornar o código um pouco mais compacto e legível. >> % mynumber deve ser 0.0 apos a proxima declaracao mynumber = sqrt(7)^2 - 7; % Estabeleca um limite de tolerancia tolerance = 0.000001;

Weeks 002.indd 52

06/03/12 09:20

MATLAB®    53

% Como mynumber e muito pequeno, mas nao exatamente 0, % utilizaremos a comparacao a seguir. % % Este exemplo mostra que poderiamos utilizar a funcao abs(). if (abs(mynumber) < tolerance) sprintf(mynumber e zero) else sprintf(mynumber nao e zero) end ans = mynumber e zero >> Como vimos, esse código nos fornece a resposta que esperamos. Esse tipo de problema também pode ocorrer durante a comparação entre dois números. Podemos desejar que eles sejam declarados iguais quando estiverem suficientemente próximos entre si. Naturalmente, o grau de tolerância depende de nós. >> % compare dois numeros muito proximos mynumber1 = sqrt(7)^2 mynumber2 = 7 tolerance = 0.000001; % Essa comparacao nos fornece resultados enganosos. if (mynumber1 == mynumber2) sprintf(mynumber1 e igual a mynumber2) else sprintf(mynumber1 e mynumber2 nao sao iguais) end mynumber1 = 7.0000 mynumber2 = 7 ans = mynumber1 e mynumber2 nao sao iguais >> Portanto, de acordo com o exemplo acima, 7 não é igual a 7.0000. Naturalmente, eles deveriam ser iguais, mas o que é mostrado na tela não é exatamente o que é armazenado internamente. Novamente, esse problema pode ser resolvido alterando-se a declaração de comparação. >> % compare dois numeros muito proximos mynumber1 = sqrt(7)^2 mynumber2 = 7 tolerance = 0.000001;

Weeks 002.indd 53

06/03/12 09:20

54    CAPÍTULO 2 % Essa comparacao nos informa que os numeros sao iguais, % ate o nosso grau de tolerancia. if (abs(mynumber1 - mynumber2)< tolerance) sprintf(mynumber1 e igual a mynumber2) else sprintf(mynumber1 e mynumber2 nao sao iguais) end mynumber1 = 7.0000 mynumber2 = 7 ans = mynumber1 e igual a mynumber2 >> Essa comparação, com a tolerância inclusa, nos fornece a resposta que esperamos. Note que a função abs é utilizada. Isso nos poupa o trabalho de fazer duas comparações. Se a função de valor absoluto não fosse incluída no código acima, ele só funcionaria quando mynumber1 fosse maior ou igual a mynumber2. Naturalmente, o arredondamento ou truncamento também deve funcionar, e a subseção a seguir nos fornece exemplos dessas funções.

  2.10.2   Arredondamento e Truncamento Existem quatro funções diferentes de arredondamento e truncamento que examinaremos: round, ceil, floor e fix, cada uma das quais converte um número de ponto flutuante em um inteiro. Visto que essas funções podem trabalhar tanto com listas quanto com valores individuais, nós as mostraremos trabalhando em uma lista de números, de modo que sua função precisa se torne aparente. Primeiramente, percebemos que a função round (“arredondar”) retorna o inteiro mais próximo do número de ponto flutuante, com um limite de corte (cutoff) de 3.5. >> round([3.01, 3.49, 3.5, 3.99]) ans = 3

3

4

4

>> round([-3.01, -3.49, -3.5, -3.99]) ans = -3 rica.*

-3

-4

-4

A função floor (“piso”) retorna o inteiro à esquerda do número de ponto flutuante na linha numé-

* Em uma definição alternativa, floor arredonda para o menor inteiro mais próximo. (N.T.)

Weeks 002.indd 54

06/03/12 09:20

MATLAB®    55

>> floor([3.01, 3.49, 3.5, 3.99]) ans = 3

3

3

3

>> floor([-3.01, -3.49, -3.5, -3.99]) ans = -4

-4

-4

-4

A função ceil, cujo nome é uma abreviação de ceiling (“teto”), trabalha de maneira oposta à da função floor, fornecendo o inteiro à direita do número de ponto flutuante na linha numérica.* >> ceil([3.01, 3.49, 3.5, 3.99]) ans = 4

4

4

4

>> ceil([-3.01, -3.49, -3.5, -3.99]) ans = -3

-3

-3

-3

Por fim, a função fix executa o truncamento, eliminando a parte fracionária. >> fix([3.01, 3.49, 3.5, 3.99]) ans = 3

3

3

3

>> fix([-3.01, -3.49, -3.5, -3.99]) ans = -3

-3

-3

-3

E se desejarmos alguns dígitos após o ponto decimal? Ao lidarmos com valores monetários, poderemos querer arredondar um valor até a casa dos centavos. Descrevemos abaixo uma maneira rápida de fazer isso. >> unleadedRate = 2.994; >> amount = 5.4; % gallons >> total = unleadedRate * amount total = 16.1676

* Em uma definição alternativa, ceil arredonda para o maior inteiro mais próximo. (N.T.)

Weeks 002.indd 55

06/03/12 09:20

56    CAPÍTULO 2 >> total = round(100*unleadedRate * amount)/100 total = 16.1700 Esse exemplo tem origem em um posto de gasolina, onde um cliente compra 5.4 galões de gasolina a US$ 2.994 por galão. A primeira forma de calcular o total produz um resultado que não se traduz em dinheiro. O segundo cálculo total mostra como podemos incluir uma conversão em dois dígitos após o decimal. Podemos exibir o resultado de uma forma apropriada, independentemente de ele ter sido arredondado ou não. >> disp(sprintf(o total e $%4.2f, total)) o total e $16.17

  2.11   Dicas de Programação em MATLAB As recomendações a seguir deverão ajudá-lo(a) a começar e também se você estiver com dificuldades em prosseguir.

• Se algo não funcionar, verifique as dimensões dos dados. Pode ser que funcione com a transposição.

• Caso um traçado não apareça, é possível que os dados sejam uma matriz vazia. • O som (tal como um arquivo.wav) pode ter dois canais; portanto não se esqueça de manipular ambos.

• Se o comando wavplay não funcionar, tente sound. • Se algo requer a transformada discreta de Fourier (DFT), tente utilizar a transformada rápida de Fourier (FFT).

• A maioria dos programas deve funcionar com a edição do estudante. • Seus programas podem ser lentos – portanto seja paciente. Se você estiver trabalhando com uma grande quantidade de dados, poderá querer que seu programa trabalhe durante a noite.

• A exibição de texto na janela de comando e a criação de traçados são operações relativamente

lentas. Caso elas estejam presentes no interior de um loop, em um programa lento, avalie se são verdadeiramente necessárias. • É fácil escrever um script (uma coleção de comandos salvos em um arquivo) para executar o(s) mesmo(s) programa(s) indefinidamente, com parâmetros diferentes. • Utilize a interface gráfica de usuário do editor para realçar uma seção de código e inserir um comentário (ou removê-lo mais tarde). • O MATLAB suporta células de código – seções de um programa marcadas por dois sinais de percentagem “%%” no início de uma linha. Graças à interface de usuário, uma célula de código pode ser executada sozinha. Quando você está concentrando seus esforços de edição em uma seção de um programa, esse recurso torna-se muito útil. • Quando um comando funcionar bem, copie-o e cole-o em um arquivo. É fácil construir um programa dessa maneira, tentando coisas diferentes na linha de comando e salvando as melhores. • Se uma função apresentar um problema, insira um comentário na linha function e certifique-se de definir variáveis no lugar dos parâmetros. • Durante uma depuração, utilize o comando break para interromper um programa e verificar os valores. Ou seja, quando estiver tentando localizar um problema, insira essa palavra-chave no programa para interrompê-lo, sabendo de antemão que ela será removida mais tarde. Você também pode configurar pontos de interrupção (breakpoints) dentro do código utilizando a interface gráfica de usuário do editor, para obter o mesmo efeito. • As palavras-chave try e catch podem ser utilizadas para que seu programa lide com erros. O Capítulo 8 (“A Transformada Discreta de Wavelet”), por exemplo, inclui um programa que emprega o comando wfilters e utiliza try e catch para exibir uma mensagem curta indicando que ele requer o conjunto de ferramentas wavelets.

Weeks 002.indd 56

06/03/12 09:20

MATLAB®    57

  2.12   Exercícios de Programação em MATLAB Apresentamos a seguir diversos exercícios de programação elaborados para ajudá-lo(a) a se familiarizar com o MATLAB. Os exercícios dependem um do outro e por isso é melhor resolvê-los na ordem. Note que o termo subjetivo “suave” significa que as mudanças entre pontos sucessivos são pequenas. 1. Crie um sinal x 1, em que x 1 = 2cos(2p15t). Especifique um intervalo t grande o bastante para mostrar 2 repetições de x 1. Trace x 1. Quais são os valores mínimo e máximo de x 1 a partir do gráfico? Especifique um incremento de t pequeno o suficiente para que o traçado de x 1 pareça suave (se ele já parecer suave, torne-o ainda mais suave) e trace x 1 novamente. Resposta: Um problema comum com essa situação é que o traçado resulta em uma linha reta em vez de uma senoide. O problema deriva do argumento para a função cos, a saber, obrigar t a ir de 0 a algum limite com um comando do tipo t=0:100. Uma vez que a contagem é por incremento de 1, t é sempre um inteiro, e o argumento para a função cos é sempre um múltiplo inteiro de 2p. Como a função cos repete-se a cada 2p, a resposta é sempre a mesma. Para resolver isso, especifique uma fração para o incremento do intervalo t, tal como t=0:0.001:1. Uma senoide de 15 Hz repete-se 15 vezes por segundo. Portanto, uma repetição completa ocorre a cada 1/15 seg. Eis um traçado não suave: t = 0:0.01:(2*1/15); x1 = 2*cos(2*pi*15*t); plot(x1) Eis um traçado suave: t = 0:0.001:(2*1/15); x1 = 2*cos(2*pi*15*t); plot(x1) Os valores mínimo e máximo são −2 e +2, respectivamente. 2. Considere x 2 = x 1 + 2.5 cos(2p30t) e ajuste t conforme o necessário. Trace x 2. Novamente, a partir do traçado, quais são os valores mínimo e máximo? Resposta: t = 0:0.001:(2*1/15); x2 = 2*cos(2*pi*15*t) + 2.5 * cos(2*pi*30*t); plot(x2) Os valores mínimo e máximo são aproximadamente −2.7 e +4.5, respectivamente.

Nota: O MATLAB possui funções embutidas – min, max, mean (média), median e sort. As questões a seguir solicitam que você escreva seu próprio código para essas funções, como se elas não fossem embutidas. A ideia aqui é permitir que você ganhe alguma experiência de programação com o MATLAB, por meio de alguns problemas de fácil verificação.

3. Escreva um código em MATLAB que forneça os valores mínimo e máximo de x 1 e x 2. Quando estiver satisfeito com o código, crie funções a partir dele chamadas “mymin” e “mymax”, respectivamente.

Weeks 002.indd 57

06/03/12 09:20

58    CAPÍTULO 2 Resposta: % % Mymax : retorne o valor maximo de uma lista. % function max_val = mymax(x) % Obtenha um palpite inicial para o maximo max_val = x(1); % Agora percorra o resto da lista, % 1 elemento de cada vez for k=2:length(x) % se o valor no offset atual % for maior que o nosso maximo atual, if (x(k) > max_val) % ..entao atualize nosso maximo atual. max_val = x(k); end end % max_val sera fornecido automaticamente. Agora para o programa de descoberta do valor mínimo: % % Mymin: retorne o valor minimo de uma lista. % function min_val = mymin(x) % Obtenha um palpite inicial para o minimo min_val = x(1); % Agora percorra o resto da lista, % 1 elemento de cada vez for k=2:length(x) % se o valor no offset atual % for menor que o nosso minimo atual, if (x(k) < min_val) % ..entao atualize nosso minimo atual. min_val = x(k); end end % min_val sera fornecido automaticamente. Por fim, podemos testar essas soluções. >> mymin(x1) ans = -2 >> mymin(x2)

Weeks 002.indd 58

06/03/12 09:20

MATLAB®    59

ans = -2.6992 >> mymax(x1) ans = 2 >> mymax(x2) ans = 4.5000 4. Encontre y1[n] = x 1[n] − .9x 1[n − 1], utilizando x 1 encontrado no Exercício de Programação 3. Trace y 1 (ajustando t conforme o necessário). Resposta: A lista t não precisa ser ajustada, mas temos que ser cuidadosos em relação a n. Ou seja, x 1[n − 1] não existe quando n = 1 (o MATLAB não permitirá que indexemos uma lista na posição 0). Assim, precisamos iniciar n em 2, de modo que n − 1 seja um valor de lista válido. Além disso, note que utilizamos parênteses para o código MATLAB, no lugar dos colchetes. for n=2:length(x1) y1(n) = x1(n) - .9 * x1(n-1); end plot(y1) 5. Gere y 2[n] de modo que ela seja a média entre o valor atual e os dois valores anteriores de x 2[n]. Trace y 2. Resposta: Tal como no Exercício de Programação 4, precisamos ser cuidadosos em relação ao valor inicial de n. for n=3:length(x2) y2(n) = (x2(n) + x2(n-1) + x2(n-2))/3; end plot(y2) 6. Gere y 3[n] = x 2[n] − y 3[n − 1]. Trace y3. O que acontecerá à medida que n se aproximar de infinito? Resposta: É preciso atribuir a y 3 um valor inicial para uso. y3(1) = 0; for n=2:length(x2) y3(n) = x2(n) - y3(n-1); end plot(y3)

Weeks 002.indd 59

06/03/12 09:20

60    CAPÍTULO 2 Para vermos o que acontece quando n se aproxima de infinito, calcularemos mais valores de y 3, após o final da entrada de x 2. for n=length(x2)+1:200 y3(n) = 0 - y3(n-1); end plot(y3) Constatamos que o traçado se repete como uma senoide indefinidamente. 7. Crie uma função aleatória que retorne uma lista de m inteiros pseudoaleatórios entre um valor baixo e um valor alto, ambos especificados. Utilize a função rand* em sua função e atribua à sua solução o nome myrand. Observe que myrand deve possuir três parâmetros: o tamanho da lista resultante (m), o valor inteiro mais baixo e o valor inteiro mais alto que ele deve fornecer. Resposta: A partir da função help rand, podemos aprender como utilizar a função rand. Por exemplo, rand(1,8) retorna 8 valores aleatórios entre 0 e 1 na forma de uma lista. Os números devem variar do valor mais baixo até o mais alto; portanto multiplicamos os valores por (alto – baixo). Em seguida, somamos baixo aos resultados. low = 10; hi = 90; n = 8; round(rand(1,n)*(hi-low) + low) Agora vamos testar a solução, com 100 valores aleatórios: >> x = round(rand(1,100)*(hi-low) + low); >> plot(x) >> min(x) ans = 10 >> max(x) ans = 90 Em seguida, inserimos em uma função: % % Retorne um vetor de m valores aleatorios % entre baixo (low) e alto (high) % % Uso: result = myrand(low, high, m) % function result = myrand(low, hi, m) result = round(rand(1,m)*(hi-low) + low); 8. Com uma lista de 8 valores entre 0 e 99 (utilize myrand), escreva uma função que retorne os valores de forma ordenada, do mais baixo ao mais alto. * Derivado de random, que significa aleatório, randômico. (N.T.)

Weeks 002.indd 60

06/03/12 09:20

MATLAB®    61

Resposta: % % mysort.m % % Fornecida uma lista de numeros, ordene-os. % (Pela simplicidade, utilize um algoritmo bubble sort.* % function sorted = mysort(unsorted) sorted = unsorted; done = false; while (~done) done = true; % Percorra a lista inteira, % ate que nao haja mais trocas de posicao. for k=2:length(sorted) % verifique o valor atual % em relacao ao seu vizinho a esquerda if (sorted(k-1) > sorted(k)) % Switch the two temp = sorted(k); sorted(k) = sorted(k-1); sorted(k-1) = temp; done = false; end end end >> unsorted = myrand(0, 99, 8) unsorted = 93

91

41

88

6

35

81

1

41

81

88

91

93

>> mysort(unsorted) ans = 1

6

35

9. Calcule a média para uma lista e chame sua função de average. Resposta: Sim, isso poderia ser feito facilmente com a seguinte linha de comando: >> sum(unsorted)/length(unsorted) ans = 54.5000

* Algoritmo de ordenação por flutuação, ou por “bolha”. (N.T.)

Weeks 002.indd 61

06/03/12 09:20

62    CAPÍTULO 2 Mas, imbuídos do espírito de fazer as coisas por nós mesmos, eis uma solução alternativa. % % average.m % % Encontre a media de uma lista de numeros. % function avg = average(x) mysum = 0; for k=1:length(x) mysum = mysum + x(k); end avg = mysum/k; 10. Escreva uma função que determine se o número passado para ela é par (even) ou ímpar (odd). Sua função evenodd deve exibir o resultado na tela. Resposta: Ambas as funções ceil e floor retornam um valor inteiro. Se um valor dividido por 2 for o mesmo que ceil ou floor do número dividido por 2, ele necessariamente deverá ser par. % % evenodd.m % Indique na tela se um numero e par ou impar. % % Uso: evenodd(lista_nao_ordenada) % function evenodd(value) if ((value/2) == floor(value/2)) % O numero e par. disp(par); else % O numero e impar. disp(impar); end A seguir, um par de testes para essa função: >> evenodd(7) impar >> evenodd(8) par Uma variação interessante desse problema consiste em fazer com que ele retorne um valor booleano (true, verdadeiro, ou false, falso) em vez de exibi-lo na tela. 11. Encontre o valor mediano a partir de uma lista e chame essa função de mymedian. (Um mediano corresponde ao valor intermediário de uma lista ordenada. Se houver dois valores no centro, caso em que o número de elementos é par, o mediano deverá corresponder à media desses dois números.) Resposta: % % mymedian.m % Retorne o valor mediano de uma lista

Weeks 002.indd 62

06/03/12 09:20

MATLAB®    63

% % Uso: m = mymedian(lista_nao_ordenada) % function m = mymedian(unsorted) % Ordene a lista sorted = mysort(unsorted); L = length(sorted); k = ceil(L/2); % Verifique se existe um numero % par ou impar de elementos da lista if (k == L/2) % Existe um numero par. % Calcule a media dos numeros centrais m = (sorted(k) + sorted(k+1))/2; else % Existe um numero impar. % Simplesmente tome o numero central m = sorted(k); end 12. Obtenha a hora atual com o comando clock. Em seguida, utilize as funções disp e sprintf conforme o necessário, para exibir a hora em um formato apropriado. Resposta: time = clock; disp(sprintf(Hora atual %2d:%2d:%2d, ... time(1,4), time(1,5), round(time(1,6))))

  2.13   Outros Comandos MATLAB Úteis A seguir, mais alguns comandos MATLAB que vale a pena experimentar: sprintf(text. integer %d

float %f, 3, 2.7)

(Note que não há um sinal de ponto e vírgula após sprintf, pois queremos que o resultado apareça na tela.) plot(1:length(x), x, r) break return close all clear all who whos save load zplane fir1 vpa Comando para o traçado de uma superfície 3D: surf

Weeks 002.indd 63

06/03/12 09:20

64    CAPÍTULO 2 Comandos que trabalham com arquivos: fopen fread fwrite fprintf fprintf(1,texto para um arquivo); fscanf fclose Para encontrar a magnitude e o ângulo de um número complexo (converter em polar): abs (fornece a magnitude) angle (fornece o ângulo de fase) Comandos para trabalhar com imagens: x = imread(lena.tif, tif); figure(1) colormap(gray(256)) image(x) rgb2hsv( ) converte do padrão RGB (Red-Green-Blue, ou Vermelho-Verde-Azul) para o padrão HSI (Hue-Saturation-Intensity, ou Matiz-Saturação-Intensidade) Comandos para trabalhar com som: audiorecorder wavrecord wavread wavplay sound wavwrite Funções relativas ao tempo: tic/toc clock cputime Comandos relativos à wavelet: wfilters wavedec dwt/idwt dwt2/idwt2

  RESUMO  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Este capítulo apresenta uma visão geral do MATLAB e demonstra alguns comandos úteis. As operações com matrizes são importantes para o DSP. Na verdade, podemos implementar transformadas com multiplicações de matrizes.

Weeks 002.indd 64

06/03/12 09:20

MATLAB®    65

  EXERCÍCIOS  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Todas as questões devem ser respondidas com o MATLAB, salvo menção em contrário. Além disso, sua resposta não deve consistir em uma simples chamada para outra função (embutida). Embora seja bom conhecer tais funções, a ideia aqui é que você ganhe experiência resolvendo os problemas por si mesmo. 1. Escreva uma função MATLAB que mantenha uma lista de números em ordem decrescente (do maior para o menor). 2. Escreva uma função MATLAB que remova números duplicados. Ou seja, fornecida uma lista [5, 2, 2, 1, 4, 4, 4], a função deve retornar [5, 2, 1, 4]. Considere que a primeira ocorrência de um número é mantida, tal como no caso da lista [7, 3, 8, 3, 7], em que sua função deve retornar [7, 3, 8]. 3. Se x = [121, 145, 167, 192, 206], o que é y = 3x − 4? Seria sum(y) o mesmo valor que 3sum(x) − 4? 4. Utilize o comando plot para traçar x e y a partir do Exercício 3. Tanto x quanto y devem aparecer no mesmo gráfico. 5. Utilize o MATLAB para traçar a senoide 2 cos(2p1500t + p/2), começando em t = 0 segundo. Certifique-se de utilizar pontos suficientes para gerar um gráfico suave e mostre apenas algumas repetições. 6. Escreva uma função MATLAB chamada myfun para computar 2N2 − 4N + 6 para qualquer valor N. Qual será o resultado se N for uma lista? Se sua função não funcionar quando N for uma lista, modifique-a para que isso aconteça. Ela deve retornar uma lista com a mesma extensão. 7. Números de ponto flutuante (reais) são armazenados em binário com precisão finita em computadores. (a) Utilize o MATLAB para descobrir o menor número que podemos armazenar. Em outras palavras, podemos armazenar 1/(2n) em uma variável quando n é pequeno, mas quão grande n pode ser antes que a variável seja considerada zero? (Utilize a precisão padrão.) (b) Qual o maior número que podemos armazenar? Em outras palavras, podemos armazenar 2n em uma variável, mas quão grande n pode ser? O que acontece quando n é grande demais? (c) Em relação ao número 1 + 1/(2n), quão grande n pode ser? Sua resposta é a mesma que a da Parte (a)? Por quê ou por que não? 8. O código MATLAB a seguir deve retornar as magnitudes e as fases para uma lista de números complexos. A linha marcada como problema na prática contém dois problemas. % Esta funcao deve retornar a magnitude e a fase. % function [mag, phi] = my_function(y) % a entrada Y consiste em uma lista de numeros complexos yr = real(y); yi = imag(y); for i=1:length(yr) phi(i) = atan(yi(i)/yr(i)); % problema mag(i) = sqrt(yr(i)*yr(i)+yi(i)*yi(i)); end (a) O primeiro problema é a ambiguidade discutida no Capítulo 1, “Introdução”. Explique do que se trata e demonstre através de exemplos.

Weeks 002.indd 65

06/03/12 09:20

66    CAPÍTULO 2 (b) Para o segundo problema, considere o que acontece quando ele processa o código a seguir, que provoca um travamento! Identifique o problema. y = [ 1+2j, 3j, 4+5j ]; [xm, xp] = my_function(y); 9. Escreva um programa que mostre o complexo conjugado da entrada do usuário. Por exemplo, se o usuário inserir −5.1 + 4.3j, o programa deverá exibir −5.1 − 4.3j. 10. Escreva um programa que obtenha um número do usuário (utilizando o comando input), transforme-o em um inteiro não complexo positivo e converta-o em binário. Por exemplo, se o usuário inserir 4, seu programa deverá exibir 0100. Se o usuário inserir −5.1 + 4.3j, o computador deverá lê-lo como 5 e exibir 0101. 11. Suponha que você deseje comparar duas listas, x e y. Elas podem ter extensões diferentes, mas deverão ter a mesma extensão para a comparação (tal como sum(x-y)). Escreva uma função que estenda a mais curta das duas listas com zeros de modo que ambas passem a ter a mesma extensão. Dica: embora seja possível fazer isso com um loop, existe uma solução mais concisa. 12. Implemente um programa que encontre a transformada aditiva/subtrativa (do Capítulo 1) de uma sequência numérica fornecida. O programa deve trabalhar com apenas um nível, mas com entradas de qualquer extensão. Por exemplo, uma entrada {63, 9, 27, 54} deve retornar uma saída aditiva {72, 81} e uma saída subtrativa {54, −27}. 13. Escreva um programa que converta um número duplo em sua representação binária. (Dica: se você exibir o número tanto em hexadecimal quanto em binário, será fácil conferi-lo.)

  PROJETO  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Utilize os programas deste capítulo para traçar um grupo de senoides com base em suas frequências, magnitudes e fases. Um exemplo de solução é fornecido no site da LTC Editora, sob o nome project2.m. Experimente também o exemplo de solução project2b.m. Ele utiliza uma interface gráfica de usuário para obter os dados de entrada a partir do usuário. Tente resolver esse problema por conta própria e depois leia o texto a seguir. Uma boa solução para este (ou qualquer outro) projeto é que contenha comentários.

Existem muitas soluções possíveis. A chave aqui é encontrar a melhor função para uso a partir do código apresentado neste capítulo. Uma função, plotsinusoids, se encarrega da maior parte do trabalho para nós. Para utilizá-la, passamos para ela uma lista de magnitudes, frequências e ângulos de fase. Portanto, essa tarefa é reduzida ao ponto de apenas solicitar tais informações ao usuário para cada senoide. Primeiro, perguntamos ao usuário quantas frequências devem ser traçadas, com uma declaração de entrada. number2plot = input(Quantas frequencias devem ser tracadas? ); Em seguida, solicitamos a magnitude, a frequência e o ângulo de fase. Isso deve ser feito em um loop, e cada resposta deve ser armazenada em uma lista. O código a seguir executa essa tarefa. for k=1:number2plot mag(k) = input(sprintf(Insira a magnitude %d : , k)); freq(k) = input(sprintf(Insira a frequencia %d : , k)); phase(k) = input(sprintf(Insira o angulo de fase %d : ,k)); end

Weeks 002.indd 66

06/03/12 09:20

MATLAB®    67

Uma vez que tenham sido coletados, todos esses dados devem ser passados para a função plotsinusoids, da seguinte maneira: plotsinusoids(freq, mag, phase); Como essa solução poderia ser melhorada? Uma condição é que plotsinusoids espera uma lista de um a quatro elementos; portanto isso deve ser verificado. Uma melhor solução seria copiar a função plotsinusoids e alterá-la para permitir mais senoides. A coleta de dados apresenta outra questão com essa solução. Embora o método input seja simples, ele não é poderoso. Por exemplo, o que aconteceria se o usuário cometesse um erro ao inserir um número? Ou o que aconteceria se o usuário digitasse uma letra em vez de um número? Além disso, poderíamos solicitar ao usuário as frequências a serem traçadas até que ele nos desse um sinal que indicasse a conclusão da entrada, tal como a letra q (de quit, ou “finalizar”). Uma solução ainda melhor seria apresentar uma interface gráfica para o usuário. Embora as interfaces gráficas de usuário estejam fora do escopo deste texto, o MATLAB efetivamente fornece esse recurso, e o exemplo project2b.m mostra como fazê-lo. Ele exibe uma interface na qual usuário pode inserir números para magnitudes, frequências e fases e utiliza a função ProcessRequest.m para criar um traçado individual sempre que o usuário clica no botão Plot. Tanto a função project2b.m quanto a função ProcessRequest.m estão inclusas no site da LTC Editora.

Weeks 002.indd 67

06/03/12 09:20

Filtros

3

F

requentemente estamos interessados em manipular um sinal. Você pode, por exemplo, elevar o volume de um sistema estereofônico, reforçar os graves (sons de baixa frequência) ou ajustar os sons em outras faixas de frequência com o equalizador. Não se trata necessariamente de exemplos relativos ao processamento digital de sinais, pois também podem ser executados com componentes analógicos, mas efetivamente servem como exemplos dos modos pelos quais podemos alterar um sinal. Os filtros permitem que alteremos sinais de muitas maneiras. Como funciona uma função de filtro? Em termos arquiteturais, como criamos um filtro? Examinaremos essas e muitas outras questões neste capítulo. O gráfico superior da Figura 3.1 mostra um exemplo de sinal, as baixas temperaturas do Capítulo 1, “Introdução”. No centro, vemos a saída de um filtro passa-baixas (lowpass), ou seja, um filtro que preserva as informações de baixa frequência. Observe como essa saída se parece com o sinal original. Além disso, você pode perceber que a versão filtrada possui um valor a mais do que o original, devido ao efeito do filtro. O número de saídas do filtro é igual ao número de entradas mais o número de coeficientes de filtro menos um. (Os coeficientes de filtro serão explicados em breve.) A parte inferior da figura mostra a saída de um filtro passa-altas (highpass), que permite a rápida mudança do sinal processado. Constatamos que a maioria dos pontos está em torno de zero, indicando pouca mudança. Contudo, uma queda visível nas proximidades da saída 5 mostra como a grande mudança entre as amostras de entrada 4 e 5 torna-se codificada aqui.

Original 75 70 65 60 55

0

5

10 Saída de um filtro passa-baixas

15

75 70 65 60

0

2

4

6

8

10

12

14

16

12

14

16

Saída de um filtro passa-altas

5 0 –5 –10

0

2

4

6

8

10

Figura 3.1 Um exemplo de sinal filtrado.

69

Weeks 003.indd 69

04/04/12 18:54

70

CAPÍTULO 3

A parte superior da Figura 3.2 mostra o conteúdo de frequência original do sinal de entrada do exemplo. O padrão singular visto aqui deve sua forma à queda acentuada entre o final do sinal de entrada e as amostras nulas (zeros) utilizadas para preencher (pad) o sinal. A função sinc pode ser utilizada para modelar esse fenômeno e, muitas vezes, janelas são empregadas para minimizar esse feito ao permitirem que o sinal se eleve gradualmente no início e, em seguida, diminua gradualmente no fim. Uma função triângulo constitui um exemplo de tal janela. A parte central da Figura 3.2 exibe a resposta passa-baixas ao impulso, dando-nos uma ideia de como o filtro afetará nosso sinal. As baixas frequências passarão sem problemas, mas as altas frequências serão atenuadas (zeradas). Por exemplo, uma frequência de entrada de 60 Hz ainda estará presente na saída, mas com apenas 10%, aproximadamente, de sua potência original. Podemos perceber isso olhando para o ponto com marcação 60 sobre o eixo x e subindo até encontrar a curva de resposta passa-baixas ao impulso no ponto em torno da marcação de 10% no eixo y. O traçado inferior da Figura 3.2 exibe a resposta passa-altas ao impulso, mostrando o efeito oposto ao do traçado anterior. Esse filtro permite a passagem do conteúdo de alta frequência, mas atenua o conteúdo de baixa frequência. Note que ambos os filtros aqui não são ideais, pois suas respectivas curvas possuem uma inclinação longa e suave. Um bom filtro, segundo nossa expectativa, teria um limite de corte mais abrupto e sua curva se pareceria mais com uma onda quadrada. Esses filtros foram criados utilizandose somente dois coeficientes de filtro: [0,5, 0,5] para o filtro passa-baixas e [0,5, −0,5] para o filtro passaaltas. Esses valores em particular serão utilizados novamente, especialmente no Capítulo 8, “A Transformada Discreta de Wavelet”. Por enquanto é suficiente dizer que a pouca quantidade de coeficientes de filtro acarreta respostas ao impulso como aquelas vistas na Figura 3.2. Os filtros de resposta finita ao impulso (finite impulse response – FIR) constituem uma classe simples de filtros que executam grande parte do trabalho de processamento digital de sinais. Eles são compostos de multiplicadores, somadores e elementos de retardo. Os diagramas que contêm filtros exibem um sinal de “+” dentro de um círculo. Em termos absolutos, ele representa um somatório sempre que o número de entradas é maior que dois. Em termos arqui-

Resposta de magnitude em frequência original 1

0,5

0

0

10

20

30

40

50

60

Resposta passa-baixas ao impulso

1

0,5

0

0

10

20

30

40

50

60

50

60

Resposta passa-altas ao impulso

1

0,5

0

0

Figura 3.2

Weeks 003.indd 70

10

20

30

40

O conteúdo de frequência do sinal de exemplo e os filtros passa-baixas/altas.

04/04/12 18:54

Filtros

x[n] Atraso

x[n] Atraso

Figura 3.3

x[n – 1]

71

x[n – 1]

Atraso

x[n – 2]

Um sinal digital, atrasado, aparece como uma versão de si mesmo deslocada no tempo.

teturais, o somatório pode ser implementado com uma árvore de soma. Por exemplo, para encontrarmos a soma dos números 58, 14, 77 e 63, podemos somar os dois primeiros (58 + 14 = 72) e os dois seguintes (77 + 63 = 140), ao mesmo tempo. Em seguida, somamos os resultados para encontrar o total (72 + 140 = 212). O mesmo procedimento funciona para qualquer conjunto de quatro números. Se trabalhássemos sempre com exatamente quatro números, poderíamos interligar fisicamente (hardware) os somadores para fazer isso. Já os multiplicadores são componentes um tanto complexos e, portanto, às vezes é preferível substituí-los (se possível) por componentes mais simples. Para multiplicar por 8, por exemplo, é mais fácil deslocar o número três posições para a esquerda (uma vez que 23 = 8). Naturalmente, a desvantagem neste caso é que um registro de deslocamento não é tão flexível quanto um multiplicador, e essa manobra só funcionaria se o valor do número multiplicador fosse sempre uma potência de 2. Os elementos de retardo podem ser imaginados como registradores – na verdade, uma vez que os registradores são utilizados para armazenar um valor por um curto período de tempo, um registrador na prática configura um elemento de retardo. A presença de dois registros encadeados tem o efeito de atrasar o sinal por duas unidades de tempo. A Figura 3.3 mostra isso para o caso geral. A saída da(s) unidade(s) de retardo aparece como uma versão da entrada deslocada no tempo. Quando o segundo valor de entrada ingressa em uma única unidade de tempo, o primeiro valor de entrada sai, levando x[n] a entrar à esquerda e x[n − 1] a sair à direita. Se considerarmos um grupo de k unidades de retardo, x[n − k] sairá à medida que x[n] entrar. A expressão “resposta finita ao impulso” deve-se ao modo pelo qual o filtro afeta um sinal. Uma função impulso constitui uma entrada interessante, em que o sinal é 0 em todos os pontos, exceto naquele onde possui o valor 1 (uma única unidade). Uma resposta do filtro FIR para essa entrada possui duração finita e, portanto, ele recebe o nome de filtro de “resposta finita ao impulso”. unit impulse function x[n] = 1, when n = 0 x[n] = 0, when n = / 0

3.1

Componentes de um Filtro

Conforme mencionado anteriormente, existem somente alguns tipos de componentes diferentes que entram na montagem de um filtro – os somadores (incluindo os somatórios), os multiplicadores e as unidades de retardo. Na realidade, o filtro de resposta infinita ao impulso (IIR) também é constituído desses componentes básicos. Para examinarmos o funcionamento dos filtros, começaremos com um somador (veja a Figura 3.4) e depois trabalharemos com um multiplicador (veja a Figura 3.5). Considere o somador e o multiplicador

b[n]

a[n] Figura 3.4

Weeks 003.indd 71

c[n]

Um somador com dois sinais como entradas.

04/04/12 18:54

72

CAPÍTULO 3

b[n]

c[n]

a[n]

Figura 3.5 Um multiplicador com dois sinais como entradas.

trabalhando com somente um valor por sinal de cada vez. O índice é muito importante, e a defasagem por uma posição afeta as saídas. Voltaremos em breve a essa questão, pois uma unidade de retardo exerce esse efeito no índice.

Exemplo 3.1 Se a = [1, 2, 3, 4] e b = [2, 1, 2, 1], o que é c? (Veja a Figura 3.4.) Resposta: c[0] = a[0] + b[0], c[1] = a[1] + b[1], c[2] = a[2] + b[2], c[3] = a[3] + b[3] c[0] = 1 + 2, c[1] = 2 + 1, c[2] = 3 + 2, c[3] = 4 + 1 c = [3, 3, 5, 5] Portanto, constatamos que c[n] = a[n] + b[n].

Exemplo 3.2 Se a = [1, 2, 3, 4] e b = [2, 1, 2, 1], o que é c? (Veja a Figura 3.5.) Resposta: c[0] = a[0] × b[0], c[1] = a[1] × b[1], c[2] = a[2] × b[2], c[3] = a[3] × b[3] c[0] = 1 × 2, c[1] = 2 × 1, c[2] = 3 × 2, c[3] = 4 × 1 c = [2, 2, 6, 4] Portanto, constatamos que c[n] = a[n] × b[n].

Exemplo 3.3 Se a = [1, 2, 3, 4] e b = [2, 1, 2, 1], o que é c? (Veja a Figura 3.6.) Resposta: c = [1 × 0.5 − 2 × 0.5, 2 × 0.5 − 1 × 0.5, 3 × 0.5 − 2 × 0.5, 4 × 0.5 − 1 × 0.5] c = [−0.5, 0.5, 0.5, 1.5] Portanto, constatamos que c[n] = 0.5 × a[n] − 0.5 × b[n].

A unidade de retardo desloca um sinal. Consideramos que qualquer valor de sinal fora de nossa faixa de índice seja 0; portanto, se um sinal estiver atrasado em uma unidade, poderemos inserir um 0 no início dele. Se, por exemplo, atrasarmos o sinal x por uma unidade para formar o sinal y (veja a Figura 3.7) e se o sinal x for igual a [1, 3, 5], então o sinal y será igual a [0, 1, 3, 5]. Como esses sinais se relacionam matematicamente? x[−1] x[0] x[1] x[2]

= = = =

0, 1, 3, 5,

y[0] y[1] y[2] y[3]

= = = =

0, 1, 3, 5

Claramente, y[n] = x[n − 1]. Unidades de retardo por vezes são exibidas com z−1 no lugar de D. A razão para isso será discutida no Capítulo 7, “A Transformada z”, mas, por ora, simplesmente considere as duas como equivalentes.

Weeks 003.indd 72

04/04/12 18:54

Filtros

73

0.5 c[n]

a[n] –0.5

b[n] Figura 3.6

Um exemplo de filtro FIR com coeficientes 0.5 e −0.5.

x[n]

D

y[n]

Figura 3.7 O sinal y é uma versão atrasada de x.

3.2

Estruturas do Filtro FIR

Agora que já vimos como os componentes formam um filtro, demonstraremos alguns filtros FIR e discutiremos algumas características importantes: descrevendo filtros FIR por equações e como a função impulso da unidade trabalha com eles. A estrutura do filtro FIR é bastante regular, e, uma vez que os números correspondentes aos multiplicadores sejam conhecidos, o filtro pode ser especificado integralmente. Chamamos esses números de coeficientes de filtro. Um uso inapropriado da terminologia consiste em chamá-los também de coeficientes das saídas do filtro. Neste livro, o termo “coeficientes” será empregado para denotar exclusivamente os coeficientes de filtro. Na Figura 3.8 vemos um exemplo de filtro FIR. Note que as setas apontam somente a direção da esquerda para a direita, típica dos filtros FIR. É verdade que uma seta aponta da entrada para baixo rumo à unidade de retardo, mas, de forma significativa, não existe uma rota do lado da saída para o lado da entrada – razão pela qual o filtro é considerado de alimentação direta, ou “para frente”. Suponha que a Figura 3.8 possua uma entrada x[n] = [1, 0]. A saídas subsequentes seriam [0.5, 0.5] e zero. Encontramos essas saídas aplicando-as, em ordem, à estrutura do filtro. No instante de tempo 0, x[0] aparece como a entrada, e qualquer coisa antes disso é zero – que será o que o elemento de retardo apresentará na saída. No instante de tempo seguinte tudo se desloca para baixo, o que significa que x[0] agora será a saída do elemento de retardo, enquanto x[1] aparece como a entrada corrente.

0.5 y[n]

x[n] D

0.5

Figura 3.8 Filtro FIR com coeficientes {0.5, 0.5}.

Weeks 003.indd 73

04/04/12 18:54

74

CAPÍTULO 3

Como você expressaria y[n] como uma equação? Conforme observamos a partir do cálculo de y[0] e y[1], nós realmente temos o mesmo padrão: y[0] = 0.5x[0] + 0.5x[−1] y[1] = 0.5x[1] + 0.5x[0]. Isso generaliza-se para: y[n] = 0.5x[n] + 0.5x[n − 1]. Um suporte finito e discreto do sinal corresponde simplesmente à sua faixa de índice. Pense no suporte de um sinal como a quantidade de amostras na qual o sinal possui um valor definido (que também poderia incluir zero). Suponha que você meça a baixa temperatura ao longo de uma semana no inverno, em graus Celsius. Se as temperaturas medidas forem {2, 1, 0, −1, 0, 2, 0}, o suporte ainda será 7, mesmo que alguns dos valores sejam 0. Para a nossa análise, poderemos considerar que todos os valores antes da primeira amostra e após a última amostra são 0.

Exemplo 3.4 Na Figura 3.9, se x = {1}, o que é y? Expresse y como uma equação.

0.6 y[n]

x[n] 0.2

D

Figura 3.9

Um exemplo de filtro FIR com coeficientes 0.6 e 0.2.

Resposta: É fácil perceber que, para uma entrada igual a 1, a saída é igual a 0.6. Mas não devemos parar por aqui, pois esse 1 será a saída da unidade de retardo no próximo instante de tempo. Podemos (e devemos) considerar que todas as futuras entradas fora dos “uns” fornecidos são iguais a zero. No instante de tempo seguinte, computamos y[1] = 0.2. Para o instante de tempo subsequente, observe que a última entrada diferente de zero (ou seja, 1) não aparece mais e que, portanto, podemos parar por aqui. y = [0.6, 0.2] Para encontrarmos a equação, observamos que para a entrada n temos a saída 0.6 × (entrada corrente) + 0.2 × (entrada anterior), levando-nos à generalização: y[n] = 0.6x[n] + 0.2x[n − 1].

A Figura 3.10 mostra a forma geral do filtro FIR para K + 1 coeficientes de filtro. (Existem K + 1 deles porque começamos em 0 e contamos até K.) O número de coeficientes de filtro também é chamado de taps.* Por convenção, o número de taps é igual ao número de coeficientes de filtro. Portanto, um filtro

* Coeficientes, derivações ou multiplicações. (N.T.)

Weeks 003.indd 74

04/04/12 18:54

Filtros

75

b[0] x[n]

Entrada

y[n] D

D

... D

Saída

b[1]

b[2]

... b[K]

Chave: D Registro de dados (atraso)

Multiplicador

Somador

Figura 3.10 Forma geral do filtro FIR.

com coeficientes {b0, b1, ... , bK} possui K + 1 taps, visto que existem K + 1 coeficientes de filtro no total. Apesar disso, diz-se que ele é de ordem K. Em outras palavras, a ordem do filtro e os taps expressam a mesma ideia, mas com uma diferença de 1. Com a estrutura da Figura 3.10 [12], é possível determinar a saída. Também é possível determinar uma equação para a saída, que é y[n] = b[0]x[n − 0] + b[1]x[n − 1] + b[2]x[n − 2] + ... + b[K]x[n − K]. Observe que, qualquer que seja o índice utilizado para b[·], ele também é utilizado em x[n − ·]. Isso significa que podemos representar tudo no lado direito da equação como um somatório.

Exemplo 3.5 Fornecidos b = [0.1, 0.2, 0.3] e x = [1, 0, 0, 2, 0, 1, 4, 3], o que é y? Resposta: y[n] = x[n − 2]b[2] + x[n − 1]b[1] + x[n]b[0] A resposta pode ser encontrada utilizando-se a equação acima e criando-se um gráfico em seguida. y[0] y[1] y[2] y[3] etc.

Weeks 003.indd 75

= 0 b[2] = 0 b[2] = x[0]b[2] = x[1]b[2]

+ 0 b[1] + x[0]b[1] + x[1]b[1] + x[2]b[1]

+ + + +

x[0]b[0] x[1]b[0] x[2]b[0] x[3]b[0]

04/04/12 18:54

76

CAPÍTULO 3

Ao inserirmos os valores de x[n] para formar colunas multiplicadas por um escalar, teremos:

Portanto, y[n] = [0.1, 0.2, 0.3, 0.2, 0.4, 0.7, 0.6, 1.4, 1.8, 0.9].

Isso nada mais é do que uma matriz multiplicada por um vetor. Em outras palavras,

Se generalizarmos isso para quaisquer x com oito elementos e b com três elementos, teremos:

Podemos escrever um dos valores de y − digamos, no índice 5 − como: y[5] = x[3] × b[2] + x[4] × b[1] + x[5] × b[0]. Com a solução da equação acima em mente, observe que esta equação y[n] = x[n − 2]b[2] + x[n − 1]b[1] + x[n − 0]b[0] na verdade é a mesma que

com bk significando a mesma coisa que b[k].

Weeks 003.indd 76

04/04/12 18:54

Filtros

77

Chamamos esse processo de convolução e utilizamos o símbolo “*” (um asterisco) para representálo. Poderíamos afirmar que y[n] = b * x[n], e o argumento considerado para x é [n − k].

Exemplo 3.6 Dado b = [0.3, 0.9, 0.4, 0.7] e

esboce o filtro que implementa essa equação. Certifique-se de rotulá-lo corretamente. Resposta: A resposta é exibida na Figura 3.11.

0.3 x[n]

y[n] D

0.9

D

0.4

D

0.7

Figura 3.11 Um exemplo de filtro FIR.

Um filtro FIR pode ser representado como um retângulo contendo números, como na Figura 3.12. A razão para tal é facilitar o trabalho (em um nível abstrato) com filtros FIR.

1, –1

Figura 3.12 Uma representação de um filtro FIR.

O impulso unitário é uma entrada muito especial, por possuir um único valor diferente de zero de uma unidade. Uma característica interessante é que ela nos fornece os coeficientes de filtro de um filtro FIR. Chamamos de h[n] a resposta ao impulso unitário. Na verdade, h é igual a b, o que significa que a resposta ao impulso é a mesma que os coeficientes de filtro, ao menos para um filtro FIR. A seguir, demonstramos uma forma de efetuar a convolução à mão, de um modo análogo à multiplicação (consulte McClellan et al. [7]). Primeiramente, alinhe dois sinais à esquerda, um acima do outro.

Weeks 003.indd 77

04/04/12 18:54

78

CAPÍTULO 3

Isso também funciona com coeficientes de filtro. Em seguida, multiplique o primeiro valor do segundo sinal por cada um dos valores do primeiro e escreva os resultados abaixo, começando da esquerda. Repita esse procedimento para cada um dos valores do segundo sinal, endentando os resultados. Por fim, some os valores ao longo de cada coluna. Suponha, por exemplo, que o sinal x = [1, 2, 3, 4, 5] seja a entrada para um filtro FIR com coeficientes b = [6, 7, 8]. Qual a saída y? Se seguirmos esse algoritmo, geraremos isso: 1 2 3 4 5 6 7 8 ------------6 12 18 24 30 7 14 21 28 35 8 16 24 32 40 ------------------6 19 40 61 82 67 40 Portanto, encontramos y = [6, 19, 40, 61, 82, 67, 40]. Note que obtemos a mesma resposta independentemente de qual dentre os dois sinais decidirmos colocar na primeira linha. Em outras palavras, chegaremos aos mesmos valores para y se convolvermos [6, 7, 8] com [1, 2, 3, 4, 5] em vez de convolvermos [1, 2, 3, 4, 5] com [6, 7, 8]. Isso pode ser confirmado como segue: 6 7 8 1 2 3 4 5 ------------6 7 8 12 14 16 18 21 24 24 28 32 30 35 40 ------------------6 19 40 61 82 67 40 Note que os números no centro são os mesmos, levando aos mesmos resultados.

3.3

Causalidade, Linearidade e Invariância no Tempo

Um sistema pode possuir três propriedades notáveis: 1. causalidade 2. linearidade 3. invariância no tempo Um sistema é causal quando utiliza exclusivamente entradas correntes e/ou prévias para calcular a saída corrente – ou seja, se a equação que descreve as entradas para as saídas for fornecida com índices de n no lado esquerdo da equação e n, n − 1, n − 2 etc. no lado direito. Da mesma forma, dizemos que um sinal é causal quando possui somente zeros para valores de índice menores que zero. Em outras palavras, ele começa no índice 0 ou após o mesmo.

Exemplo 3.7 Quais dos filtros a seguir são causais? (Considere que K é positivo.)

Weeks 003.indd 78

04/04/12 18:54

Filtros

79

Resposta: sim, não, não, não, sim, sim, sim, não, não

Quando um sistema exibe uma saída expressa como uma soma de entradas multiplicadas por constantes, como aquela com filtros FIR, ele é considerado linear. Um sistema linear possui propriedades de escalonamento e de aditividade. O escalonamento significa que obtemos o mesmo resultado multiplicando a entrada por uma constante ou a saída por essa mesma constante, como na Figura 3.13. Para um sistema linear, y1 = y2. Suponha, por exemplo, que tenhamos um sistema y[n] = 2x[n] + x[n − 1]. Se substituíssemos cada valor de x por ax, calcularíamos 2ax[n] + ax[n − 1] para encontrar nossa saída. Isso é o mesmo que a(2x[n] + x[n − 1]), ou seja, o mesmo que obteríamos se a saída fosse multiplicada por a, que é precisamente o que escalonamento significa. A aditividade significa que podemos somar dois sinais antes da aplicação de um filtro linear ou que podemos aplicar o filtro linear a cada sinal e somar seus resultados (veja a Figura 3.14), isto é, y3 = Sistema(x1 + x2) = y1 + y2. De qualquer modo, terminamos com a mesma resposta. a x[n]

y1[ n]

Sistema a

x[n]

Figura 3.13

y2[ n]

Sistema

Condição linear 1: propriedade de escalonamento.

x1[ n]

x2[ n]

x1[ n]

Sistema

y1[ n]

Sistema

y2[ n]

Sistema

y3[ n]

x2[ n] x1[ n]

x3[ n]

Figura 3.14 Condição linear 2: propriedade aditiva.

Weeks 003.indd 79

04/04/12 18:54

80

CAPÍTULO 3

Essas propriedades são válidas desde que as entradas sejam multiplicadas por constantes (isto é, não multiplicadas entre si). Ou seja, o sistema descrito por y[n] = c0x[n] + c1x[n − 1] + c2x[n − 2] + ... + cKx[n − K] é linear (y[n] é a saída corrente, x[n] é a entrada corrente, x[n − 1] é a entrada anterior, e todos os valores c são constantes). Quando dois desses sistemas são interligados em série, a ordem não é importante. Considere, por exemplo, o sistema y1[n] = 2x[n] e o sistema y2[n] = x[n] + x[n − 1], ambos lineares. Se x[n] for operado pelo sistema 1 e o resultado (y1[n]) for operado pelo sistema 2, o resultado será z1[n] = (2x[n]) + (2x[n − 1]). Se mudássemos a ordem dos sistemas, teríamos z2[n] = 2(x[n] + x[n − 1]). Como se pode constatar, z1 e z2 são iguais. Como um contraexemplo de linearidade, considere o sistema em que y[n] = x[n]2. Considere y1[n] = x1[n] × x1[n] e y2[n] = x2[n] × x2[n]. Se definirmos x3[n] = x1[n] + x2[n], encontraremos y3[n] = (x1[n] + x2[n]) × (x1[n] + x2[n]) = x1[n]2 + x2[n]2 + 2x1[n]x2[n]. Claramente, isso não é o mesmo que y1[n] + y2[n]. Portanto, esse sistema não é linear. A invariância no tempo significa que a saída de um sistema reflete a mesma defasagem do sinal de entrada. Ou seja, se a entrada x[n] for atrasada em k unidades, ela aparecerá como x[n − k] e a saída y[n] aparecerá como y[n − k]. Outra maneira de dizer isso é que os resultados são os mesmos, independentemente de a entrada ser atrasada ou a saída ser atrasada. Os filtros FIR são invariantes no tempo, desde que seus coeficientes não mudem com o tempo. (Os filtros adaptativos são uma exceção, pois mudam seus coeficientes de acordo com a entrada – por exemplo, para tentar prever a entrada seguinte.) Um sistema que não é invariante no tempo é o subamostrador.* Um subamostrador descartará valores, como uma subamostragem por 2 descarta cada segundo valor. Um atraso na entrada em uma unidade de tempo significa que a saída do subamostrador seria constituída pelos valores com índice ímpar, em vez dos pares.

Exemplo 3.8 Suponha que o sistema1 seja um filtro FIR com h1[n] = {1, 1, 1, 1} e que o sistema2 seja um filtro FIR com h2[n] = {2, 0, 2}. Considere x[n] = {2, 4, 6, 8, 10}. Se saída1 = h2 * (h1 * x) e saída2 = h1 * (h2 * x), qual é a relação entre saída1 e saída2? Por quê? Resposta: Elas são iguais. Tanto sistema1 quanto sistema2 são lineares e invariantes no tempo (linear and time-invariant – LTI), de modo que o efeito das operações de convolução é equivalente a (h2 * h1) * x.

Teste de Causalidade:

• Expresse o sistema em termos de uma equação de entrada/saída. Por exemplo, y[n] = sistema(x) = 2x[n] − 3x[n − 1].

• Examine os índices de entrada versus os índices de saída. Se um ou mais índices de entrada for maior do que o índice de saída, o sistema será não causal. Caso contrário, ele será causal. Por exemplo, o índice para a saída y é n, enquanto os índices para a entrada x são n e n − 1. Como n> −nen> − n − 1, sabemos que esse sistema é causal. Teste de Linearidade:

• Expresse o sistema em termos de uma equação de entrada/saída – por exemplo, sistema(x) = 2x[n] − 3x[n − 1].

• Teste a × sistema(x) quanto à igualdade em relação a sistema(a × x). Por exemplo, sistema(a × x)

= = = =

2 a a a

(ax[n]) − 3 (ax[n − 1]) (2x[n]) + a(−3x[n − 1]) (2x[n] − 3x[n − 1]) × sistema(x).

• Teste sistema(x1 + x2) quanto à igualdade em relação a sistema(x1) + sistema(x2). Por exemplo,

* Downsampler. Na prática, um redutor de taxa de amostragem. (N.T.)

Weeks 003.indd 80

04/04/12 18:54

Filtros

sistema(x1 + x2) = = = =

81

2(x1[n] + x2[n]) − 3(x1[n − 1] + x2[n − 1]) 2x1[n] + 2 x2[n] − 3x1[n − 1] − 3x2[n − 1] (2x1[n] − 3x1[n − 1]) + (2x2[n] − 3x2[n − 1]) sistema(x1) + sistema(x2).

Ambos os testes deram positivo e, portanto, esse sistema é linear. Teste de Invariância no Tempo:

• Expresse o sistema em termos de uma equação de entrada/saída. Por exemplo, sistema(x) = 2x[n] − 3x[n − 1].

• Examine o sistema para verificar se um número arbitrário de atrasos exerce o mesmo impacto independentemente de ter sido aplicado à entrada ou à saída. Em outras palavras, teste atraso(sistema(entrada)) quanto à igualdade em relação a sistema(atraso(entrada)). Note que devemos considerar todas as funções atraso(); se o teste falhar para um atraso de uma unidade de tempo mas não para um atraso de duas unidades, devemos concluir que o sistema não é invariante no tempo. Por exemplo, o atraso de x por d unidades de tempo significa que substituímos o índice n por n − d. Portanto, atrasod(x) = x[(n − d)], o que significa que sistema(atrasod(x)) = 2x[(n − d)] − 3x[(n − d) − 1]. A partir da equação de entrada/saída anterior sabemos que sistema(x) = 2x[n] − 3 x[n − 1], de modo que o atraso disso produz atrasod(sistema(x)) = 2x[(n − d)] − 3x[(n − d) − 1]. Os sistemas LTI são implementados com convolução, e o operador de convolução é comutativo; isso significa que a ordem dos operandos não é importante, tal como em h1 * h2 = h2 * h1. Ele também é associativo, conforme vimos anteriormente, o que significa que obtemos a mesma resposta com três operandos, não importando que par convolvamos primeiro, h2 * (h1 * x) = (h2 * h1) * x. A convolução também possui a propriedade distributiva [13]. Por exemplo, com a multiplicação e a adição, sabemos que a(b + c) = ab + ac. Isso também funciona com a convolução e a adição, tal como em x * (h1 + h2) = x * h1 + x * h2. Note que a adição não é distributiva em relação à convolução. Assim como a + (bc) = / (a + b)(a + c) (exceto em álgebra booleana), x + (h1 * h2) = / (x * h1) + (x * h2). Para demonstrar isso, faça um teste com um conjunto bem simples de sinais, tal como x = {x0, x1, x2}, h1 = {a} e h2 = {b}.

3.4

Unidades de Multiplicação e Acumulação

Uma Unidade de Multiplicação e Acumulação (multiply accumulate cell – MAC) consiste em uma estrutura regular que efetua uma multiplicação e uma adição. Quando se trata do projeto de hardware, regularidade é importante. A Figura 3.15 exibe uma unidade de multiplicação e acumulação. Como o nome indica, ela executa uma operação de multiplicação em sua entrada e alimenta o somador com o resultado. Coeficiente de filtro

Entrada anterior Resultado anterior

Weeks 003.indd 81

D

Figura 3.15 Unidade de multiplicação e acumulação.

04/04/12 18:54

82

CAPÍTULO 3

a0

x[n]

a1

D

a2

D

D

0

Figura 3.16

Unidades de multiplicação e acumulação como um filtro.

As MACs podem ser dispostas em fila, conforme mostrado na Figura 3.16. Para alterar esse filtro aumentando o número de taps, simplesmente adicione mais MACs à direita da fila. A partir dessa figura, é fácil verificar que ela coincide com a forma geral do filtro FIR – apenas reorganize-a para que os somadores fiquem alinhados no topo e as unidades de retardo fiquem na parte de baixo à esquerda. Quando x[n] aparece na entrada superior esquerda da Figura 3.16, ramifica-se para a entrada da unidade de retardo e para uma entrada do multiplicador. O multiplicador encontra o resultado de a0x[n] e o repassa para o somador, que possui outra entrada 0. Assim, a primeira unidade produz o valor prévio de x[n] na parte de cima (ou seja, x[n − 1]), enquanto produz a0x[n] na parte de baixo. A segunda unidade repassa x[n − 1] para sua unidade de retardo e para seu multiplicador, que por sua vez calcula a1x[n − 1]. Seu somador encontra o resultado de a0x[n] + a1x[n − 1]. Em seguida, a terceira unidade recebe em sua entrada superior a saída da unidade de retardo da segunda unidade. Trata-se da versão atrasada da versão atrasada da entrada corrente, x[n − 2]. O multiplicador encontra a2x[n − 2] e o adiciona à soma do somador anterior. Desse modo, ele encontra a0x[n] + a1x[n − 1] + a2x[n − 2], a convolução dos valores ak e x. Note que tudo isso acontece simultaneamente. Os multiplicadores e somadores precisam de algum tempo para operar e para que os resultados dos somadores sejam transportados da esquerda para a direita, de modo que a velocidade do projeto global será baseada nisso. Após um período determinado, a saída estará estável e as unidades de retardo poderão repassar seus valores x para a próxima unidade de retardo na sequência. As unidades de retardo armazenam as entradas para as unidades de multiplicação e acumulação e permitem que elas trabalhem como um canal de processamento de dados. Os elementos de processamento (processing elements – PEs) menos padronizados são semelhantes às MACs. Os PEs são modulares como as MACs, embora suas operações não sejam fixas.

3.5

Resposta de Frequência de Filtros

Os filtros FIR podem implementar diversas funções diferentes simplesmente mudando-se os coeficientes de filtro. As funções de um filtro dependem de como ele trata um sinal de entrada no domínio da frequência. Ou seja, um sinal de entrada terá frequências diferentes, e o filtro não interferirá em algumas e descartará outras. Portanto, um filtro pode ser descrito como passa-baixas (lowpass), passa-altas (highpass), passa-banda (bandpass), rejeita-banda (bandstop) ou notch,* dependendo das frequências que passam pelo filtro e permanecem relativamente inalteradas. Embora o termo literal para bandstop seja “bloqueio de faixa”** algumas pessoas preferem o termo “rejeita-banda” pelo fato desses filtros rejeitarem uma banda de frequências. Além disso, a palavra notch (entalhe) frequentemente é empregada simplesmente como nome alternativo para um filtro rejeita-banda, embora os filtros notch geralmente rejeitem uma faixa de frequências muito estreita. Existem outros tipos de filtros, tais como os filtros diferenciadores, os filtros de desvio de fase e os filtros adaptativos, mas eles estão fora do escopo deste capítulo. Além disso, um sistema consiste em um superconjunto de um filtro. Em outras palavras, um filtro é um tipo de sistema, mas um sistema não é necessariamente um filtro.

* Um tipo de filtro rejeita banda, também conhecido como filtro de entalhe. (N.T.) ** Ou de “bloqueio de banda”. (N.T.)

Weeks 003.indd 82

04/04/12 18:54

Filtros

83

Filtro passa-baixas com frequência de corte de 30% 1 0.8 0.6 0.4 0.2 0 0

0.1 Figura 3.17

0.2

0.3

0.4

0.5

0.6

0.7

0.8

0.9

1

Resposta de magnitude em frequência para um filtro passa-baixas.

Filtro passa-altas com frequência de corte de 50% 1 0.8 0.6 0.4 0.2 0 0

0.1 Figura 3.18

0.2

0.3

0.4

0.5

0.6

0.7

0.8

0.9

1

Resposta de magnitude em frequência para um filtro passa-altas.

As operações de filtragem permitem que as frequências que desejamos passem pelos filtros, ao mesmo tempo diminuindo a presença de frequências indesejadas. Chamamos essas frequências não desejadas de ruído. Imagine, por exemplo, que uma pessoa fale durante a emissão um som extremamente agudo. Se gravarmos a pessoa, captaremos o som agudo também. Mas, se a voz da pessoa possuir frequências mais baixas do que o som agudo, poderemos utilizar um filtro passa-baixas para que ela se sobressaia. Os termos passa-baixas e passa-altas são razoavelmente autoexplicativos. Um bom exemplo de resposta de magnitude em frequência de um filtro passa-baixas é mostrado na Figura 3.17, enquanto um exemplo de resposta de magnitude em frequência para um filtro passa-altas pode ser visto na Figura 3.18. Ao longo do eixo x temos uma percentagem das frequências de 0 a 1, embora as frequências efetivas dependam da taxa de amostragem fs e do intervalo de 0 Hz a fs/2 Hz, o que é explicado nos Capítulos 4 e 5, “Senoides” e “Amostragem”, respectivamente. Assim, o que torna as Figuras 3.17 e 3.18 boas representantes de filtros passa-baixas/altas? Ambos os exemplos mostram claramente o corte abrupto entre a banda passante* (as frequências que passam através do filtro) e a banda de corte (as frequências que o filtro atenua). Para uma representação desses termos, examine a Figura 3.19. Os filtros possuem uma banda de transição entre a banda passante e a banda de corte, onde a resposta de magnitude em frequência cai de aproximadamente 100% para aproximadamente 0%. Precisamos, contudo, ser cuidadosos em dizer “aproximadamente”, pois os filtros apresentam ripple de banda passante e ripple de banda de corte. Em vez de uma resposta de magnitude constante na banda passante, por exemplo, a resposta efetivamente ondula para cima e para baixo de forma muito parecida com uma onda senoidal, chamada ripple. Quando especificamos a banda de transição, incluímos esses

* Passband, também conhecida como “banda de passagem”. (N.T.)

Weeks 003.indd 83

04/04/12 18:54

84

CAPÍTULO 3

Ripple de banda passante 0.8 0.6 0 0

0.1

0.2

0.3

0.4

0.5

0.6

0.7

0.8

0.9

1

0.7

0.8

0.9

1

0.7

0.8

0.9

1

Banda de transição 1

0.5

0 0 3

0.1

0.2

0.3

⫻ 10 ⫺3

0.4

0.5

0.6

Ripple de banda de corte

2 1 0 0

0.1

Figura 3.19

0.2

0.3

0.4

0.5

0.6

Banda passante, banda de transição e banda de corte, exibidas com ripples.

ripples como um fator. A Figura 3.19 exibe uma visão ampliada do ripple de banda passante (gráfico superior), seguido pela banda de transição (gráfico central) e pelo ripple de banda de corte (gráfico inferior). A diferença dessa figura em relação à Figura 3.17 (além do fato de possuir uma frequência de corte diferente) é que os incrementos do eixo y são muito pequenos nos subtraçados superior e inferior. O ripple de banda passante e de banda de corte ocorre em ambas as figuras, mas simplesmente não é visível na Figura 3.17. Por que o ripple de banda de corte é mostrado como uma série de pequenos picos? O traçado que vemos consiste na resposta de magnitude em frequência, que se origina da transformada discreta de Fourier (DFT). A DFT retorna dados complexos, e utilizamos a função para encontrar a resposta de magnitude em frequência. Assim, em vez de um padrão semelhante ao senoidal em torno do eixo x, vemos somente valores positivos. Os picos na banda de corte estão ficando menores da esquerda para a direita? Sim, e isso é comum. Um filtro passa-baixas pode ser chamado de um filtro de média (averaging), enquanto um filtro passa-altas poderia ser aludido como um filtro diferencial (differencing), visto que esses termos descrevem as funções do filtro. Por exemplo, se um filtro FIR possui dois coeficientes de 0.5 e 0.5, vemos que a saída, y[n] = 0.5x[n] + 0.5x[n − 1], ou, de forma equivalente, y[n] = (x[n] + x[n − 1])/2, encontra a média das entradas prévia e corrente. Se examinarmos a resposta de frequência desse filtro de média, veremos que os componentes de baixa frequência (de mudança lenta) aparecem na saída, enquanto o filtro diminui os componentes de alta frequência (de mudança rápida). Para o filtro diferencial, teríamos coeficientes de {0.5, − 0.5}, onde y[n] = (x[n] − x[n − 1])/2. Isso fornece a mudança entre as duas amostras, escalonada por 1/2 para ser consistente com o filtro de média. Como era de se esperar, a resposta de frequência de tal filtro atenuaria o componente de mudança lenta de um sinal e acentuaria o componente de mudança rápida, levando-nos à conclusão de que se trata de um filtro passa-altas. O termo passa-banda* significa que as frequências dentro de certa banda (ou faixa) passam através do filtro, enquanto as frequências fora daquela banda são atenuadas (reduzidas em amplitude). Um filtro

* Ou passa-faixa. (N.T.)

Weeks 003.indd 84

04/04/12 18:54

Filtros

85

Filtro passa-banda com frequências de corte de 30% e 50% 1 0.8 0.6 0.4 0.2 0 0

0.1 Figura 3.20

0.2

0.3

0.4

0.5

0.6

0.7

0.8

0.9

1

Resposta de magnitude em frequência para um filtro passa-banda.

Filtro rejeita-banda com frequências de corte de 30% e 50% 1 0.8 0.6 0.4 0.2 0 0

0.1 Figura 3.21

0.2

0.3

0.4

0.5

0.6

0.7

0.8

0.9

1

Resposta de magnitude em frequência para um filtro rejeita-banda.

passa-banda, por exemplo, pode permitir que frequências entre 10 kHz e 20 kHz deixem o filtro intactas, ao mesmo tempo reduzindo significativamente quaisquer frequências abaixo de 10 kHz ou acima de 20 kHz. A Figura 3.20 exibe a resposta de magnitude em frequência de um exemplo de filtro passa-banda. Um filtro rejeita-banda* é o oposto do filtro passa-banda, atenuando frequências dentro de certa banda e deixando passar quaisquer frequências acima ou abaixo daquela banda. Na Figura 3.21 vemos um exemplo de filtro rejeita-banda, criado com os mesmos parâmetros que os do filtro passa-banda, mas, obviamente, o efeito sobre um sinal é o oposto ao do anterior. As figuras subsequentes mostram dois exemplos adicionais de respostas de filtros, como casos especiais dos filtros passa-banda e rejeita-banda. A Figura 3.22 exibe um filtro rejeita-banda com uma faixa de frequência bastante estreita sendo atenuada. Filtros notch como esse eliminam o ruído em uma frequência específica. Por fim, a Figura 3.23 exibe a resposta de magnitude em frequência para um filtro passa-banda, mas com duas bandas passantes. Tal filtro seria excelente para eliminar componentes do sinal com frequências não desejadas, permitindo que as frequências em uma banda ou na outra passassem por ele. Para examinarmos o comportamento de um filtro, podemos visualizar um traçado de sua resposta de magnitude em frequência. Para obter a resposta de magnitude em frequência, execute primeiramente a Transformada Discreta da Fourier (DFT – consulte o Capítulo 6, “A Transformada de Fourier”) nos coeficientes de filtro. Esses coeficientes podem ser complementados com amostras nulas (zero) para tornar

* Ou rejeita-faixa. (N.T.)

Weeks 003.indd 85

04/04/12 18:54

86

CAPÍTULO 3

Filtro notch com frequência de corte de 40% 1 0.8 0.6 0.4 0.2 0 0

0.1

0.2

0.3

0.4

0.5

0.6

0.7

0.8

0.9

1

0.9

1

Figura 3.22 Um filtro notch.

Filtro passa-banda com frequências de corte de 20%, 40%, 60% e 80% 1 0.8 0.6 0.4 0.2 0 0 Figura 3.23

0.1

0.2

0.3

0.4

0.5

0.6

0.7

0.8

Resposta de magnitude em frequência para um filtro passa-banda com duas bandas passantes.

as curvas mais suaves. Uma premissa é que o número de coeficientes passado para essa função é inferior a 128, uma conveniente potência de 2. O exemplo a seguir examina dois filtros, um passa-baixas e um passa-altas. Mostramos somente a primeira metade do espectro. A segunda metade será somente uma imagem espelhada, visto que o sinal de entrada é real. % Mostre a resposta de magnitude em frequencia para os coeficientes de um filtro FIR % Consideramos que o numero de coeficientes e inferior a 128. % function freq_mag_resp(h) % Insira zeros para que os resultados tenham boa aparencia x = h; x(length(h)+1:128) = 0; % Obtenha a resposta de magnitude em frequencia de x X = fft(x); X_mag = abs(X); % Mostre os resultados half = ceil(length(X_mag)/2); plot(1:half, X_mag(1:half), 'b'); title('resposta de magnitude em frequencia');

Weeks 003.indd 86

04/04/12 18:54

Filtros

87

O projeto do filtro é discutido no Capítulo 10, “Aplicações”. O MATLAB disponibiliza algumas funções para isso na caixa de ferramentas de processamento de sinais, incluindo fir1 e fir2. O gráfico da Figura 3.21, por exemplo, pode ser construído com o código a seguir. Observe que esse código faz praticamente a mesma coisa que a função freq–mag–resp acima, mas com um código mais compacto. % Obtenha os coeficientes do filtro rejeita-banda B2 = fir1(100, [0.3, 0.5], 'pare'); x = zeros(1, 1000); x(50) = 1; Y2 = fft(conv(x,B2)); half = 1:ceil(length(Y2)/2); plot(half/max(half), abs(Y2(half)), 'b'); Para criar um filtro passa-altas como o da Figura 3.18, modifique o código anterior para utilizar coeficientes de filtro diferentes: B4 = fir1(100, 0.5, 'altas'); Além disso, a resposta de magnitude em frequência da Figura 3.23 pode ser encontrada com o filtro a seguir. B5 = fir1(100, [0.2, 0.4, 0.6, 0.8], 'passa-banda');

3.6

Filtros IIR

O filtro de resposta finita ao impulso (FIR) utiliza exclusivamente cálculos de alimentação direta. Se permitíssemos a realimentação, a resposta do filtro a um impulso não seria necessariamente finita. Portanto, os filtros realimentados são chamados de filtros de resposta infinita ao impulso (infinite impulse response – IIR). Considere o filtro representado na Figura 3.24. A equação que descreve sua saída é: y[n] = 0.4y[n − 1] + 0.6 x[n] + 0.2x[n − 1]. Muitas vezes, os termos da saída atrasada aparecem primeiro, como na equação anterior. A saída y[ ] aparece em ambos os lados da equação. Isso significa que a saída corrente depende parcialmente da saída prévia. Se uma função impulso passar através desse filtro, teremos as seguintes saídas: entrada 0 1 0 0 0 0 0

saída 0 0.6 0.44 1.176 0.0704 0.02816 0.011264

A saída torna-se progressivamente menor e se aproxima de zero. Retorne os limites das funções – trata-se de um procedimento importante para a análise de tais filtros. Para esse filtro, visto que todas as futuras entradas de função impulso serão 0, as futuras saídas corresponderão a 40% da saída anterior, devido a esse coeficiente de realimentação específico (0.4). Em teoria, a saída nunca chegará a zero, mas, em termos práticos, um filtro digital possui precisão finita; portanto haverá um momento em que ele produzirá uma resposta igual a zero.

Weeks 003.indd 87

04/04/12 18:54

88

CAPÍTULO 3

0.6 x[n]

y[n] D

0.2

0.4

D

Figura 3.24 Um filtro com realimentação.

0.6 x[n]

y[n] D

0.2

1

D

Figura 3.25 Outro filtro com realimentação.

Considere, entretanto, o filtro da Figura 3.25. À primeira vista ele parece igual ao da Figura 3.24. A diferença é pequena, mas significativa: os coeficientes são diferentes. Especificamente, o coeficiente de realimentação é igual a 1. A saída para esse filtro é: entrada 0 1 0 0 0

saída 0 0.6 0.8 0.8 0.8

O filtro produzirá a mesma saída, 0.8, desde que a entrada permaneça igual a zero. Trata-se de um filtro que, tal como o nome sugere, proporciona uma resposta infinita àquele impulso. Os filtros de resposta infinita ao impulso, como o mostrado na Figura 3.26, podem apresentar saídas não muito bem comportadas. Mais uma vez, o coeficiente de realimentação foi alterado, dessa vez para 1.1.

0.6 x[n]

y[n] D

0.2

1.1

D

Figura 3.26 Um terceiro filtro com realimentação.

Weeks 003.indd 88

04/04/12 18:54

Filtros

89

A saída para o filtro da Figura 3.26 é: entrada 0 1 0 0 0 0 0

saída 0 0.6 0.86 0.946 1.0406 1.14466 1.259126

A saída continuará crescendo em magnitude. Isso demonstra um problema com os filtros IIR: a saída pode se aproximar de infinito (positivo ou negativo). Também é possível para a saída de um filtro IIR oscilar rumo ao infinito – que a saída se torne maior em magnitude, embora o sinal possa mudar. Dizemos que um filtro é estável quando a saída se aproxima de zero uma vez que a entrada caía a zero. Se a saída oscila a uma taxa constante, dizemos que o filtro é condicionalmente estável. Se a saída se aproxima; do infinito positivo ou negativo (tal como no exemplo anterior) ou se ela oscila rumo ao infinito, dizemos que o filtro IIR é instável. A forma geral do filtro IIR é mostrada na Figura 3.27 [12]. Outros textos se referem a ela como forma direta I, devido à correspondência direta entre a figura e a equação que a descreve. Por inspeção, você deve ser capaz de escrever a equação. Por outro lado, você deve ser capaz de desenhar o filtro tendo em mãos a equação para ele.

b[0] Entrada

b[0]

x[n]

w[n] D

D

... D

y[n]

b[1]

a[1]

b[2]

a[2]

...

...

b[K]

a[L]

Saída

D

D

... D

Chave: D Registro de dados (atraso)

Multiplicador

Somador

Figura 3.27 Forma geral do filtro IIR.

3.7

Tendências de um Filtro IIR Simples

Considere um filtro IIR simples, como o da Figura 3.28. Ao examinarmos a figura, podemos encontrar a relação de entrada/saída como: y[n] = bx[n] + ay[n − 1].

Weeks 003.indd 89

04/04/12 18:54

90

CAPÍTULO 3

b x[n]

y[n] a

D

Figura 3.28 Um filtro IIR simples.

Para uma função impulso como entrada, somente o valor inicial de y seria diretamente impactado por x. Ou seja, considerando-se o sistema inicialmente em repouso (y[−1] = 0), computamos a primeira saída: y[0] = bx[0] + 0. Para a função impulso, somente x[0] possui um valor diferente de zero e x[0] = 1. Desse modo, a saída se torna: y[0] = b e y[n] = ay[n − 1],

n > 0.

A aparência da saída com o tempo depende diretamente do valor de a. O código a seguir nos fornece a saída, mostrada na Figura 3.29.

a = –0.5

a = –0.5

10

10

5 5 0 –5 0

5

10

15

20

0 0

5

a = –1 10

0

10

2

x

5

10

15

20

9 0

a = –2

107

15

1

10

0

5

–1 0

5

10

15

20

15

20

15

20

a=1

10

–10 0

10

15

20

x

0 0

5

10 a=2

106

5

10

Figura 3.29 Saída de um filtro IIR simples.

Weeks 003.indd 90

04/04/12 18:54

Filtros

91

N = 20; y(1) = 10; for n=2:N+1 y(n) = a * y(n-1); end A partir dos gráficos, constatamos que a saída se aproxima de zero (para os valores a = −0.5 e a = 0.5), oscila (a = −1), permanece constante (a = 1) ou cresce em magnitude para saídas sucessivas (a = −2 e a = 2). Mais à frente, no Capítulo 7, veremos por que esse é o caso.

3.8

Correlação

A correlação determina o nível de semelhança entre dois sinais, utilizando a convolução. Aqui, consideramos exclusivamente sinais de valores reais, embora seja possível encontrar a correlação entre sinais de valores complexos. A correlação por si só gera um único número, chamado de coeficiente de correlação [14]. Um coeficiente de correlação grande e positivo indica uma relação entre os sinais, enquanto um coeficiente de correlação próximo de zero significa que os dois sinais não são correlatos (ou que não estão apropriadamente alinhados). Um número negativo indica uma correlação negativa, o que significa que um sinal tende a decrescer à medida que o outro cresce [15]. Quando os sinais de entrada não estão alinhados entre si, um único cálculo de correlação pode dar a impressão de que os dois sinais não são correlatos. Para resolver isso, um dos sinais deve ser deslocado, mas não se sabe o quanto. Naturalmente, um observador humano poderia dizer como um sinal deveria ser deslocado para se alinhar com o outro, ao menos nos casos simples. Para um sistema automatizado, um sinal é deslocado até N vezes, e o valor máximo para a correlação cruzada é escolhido. A correlação pode ser executada com convolução, revertendo-se um sinal e encontrando-se o conjugado do outro, caso ele seja complexo. Suponha que desejemos comparar o sinal x com o sinal y. Em vez de considerarmos o sinal y[k] sendo indexado a partir de 0 até algum limite positivo K, nós o rebateremos em torno da amostra no zero e consideraremos que ele começa em −K. Por exemplo, se y[n] = {5, 3, 1, 6}, interpretamos isso como y[0] = 5, y[1] = 3, y[2] = 1 e y[3] = 6. Se rebatermos esse sinal, teremos y[−3] = 6, y[−2] = 1, y[−1] = 3 e y[0] = 5. Quando convolvermos y com x, somaremos x[n] × y[n − k]. Ao utilizarmos a versão rebatida de y, a única coisa a ser alterada é o índice – por exemplo, x[n] × y[n − (− k)] ou simplesmente x[n] × y[n + k]. O sinal de saída resultante da correlação é conhecido como correlação cruzada, exceto no caso em que o mesmo sinal é utilizado para ambas as entradas, o que é chamado de autocorrelação [13]. Também introduzimos a covariância cruzada e a autocovariância, dependendo do uso de dois sinais diferentes ou do mesmo sinal em ambas as entradas, respectivamente. As covariâncias farão duas coisas para nós: a covariância cruzada nos fornecerá um valor muito próximo ao da correlação cruzada, e as autocovariâncias nos permitirão escalonar o resultado de tal modo que ele tenha um valor máximo de 1. O número de amostras de dados influencia o cálculo da correlação, e portanto a correlação cruzada deve ser dividida por N, o número de amostras de dados [15]. Começaremos com um valor bruto para a correlação que efetivamente corresponde a uma versão simplificada da próxima equação para a covariância cruzada.

A equação anterior fornece uma aproximação para sx,y[k], uma vez que não leva em conta a média do sinal e considera que ele é 0 tanto para x quanto para y. Se as médias do sinal forem diferentes de zero, precisaremos utilizar a equação a seguir (covariância cruzada) em seu lugar:

Weeks 003.indd 91

04/04/12 18:54

92

CAPÍTULO 3

A obtenção de uma boa estimativa para a correlação cruzada significa que precisamos encontrar também a autocovariância, tanto para x quanto para y:

Para encontrar sy,y, utilize a equação acima para sx,x e substitua x por y. Estima-se que a correlação cruzada seja:

Para a autocorrelação, ␳x,x[k], substitua y por x. Encontramos sx,x[k] substituindo ambos os parâmetros por x na equação para sx,y[k].

Observe que ␳x,y[k] é uma estimativa da correlação, pois estamos lidando com dados amostrados, e não com os processos básicos que geram esses dados. Como você pode ter adivinhado, tais fórmulas se originam das áreas de probabilidade e estatística. A correlação cruzada é relacionada à variância, ␴2. Para maiores informações, consulte um livro como Probability and Statistics for Engineers and Scientists, de Ronald E. Walpole e R. H. Myers [14].

3.8.1

Uma Expressão Concisa para um Valor de Correlação Aproximado

Citamos a seguir alguns exemplos de correlação. Temos aqui x e y, dois sinais idênticos, porém deslocados entre si. Para computar a correlação, utilizamos uma aproximação simples. Primeiramente, encontramos esta multiplicação ponto a ponto de x e y, com os códigos a seguir: x*y.' Isso fornece uma expressão concisa. Note que utilizamos a transposição de y. Caso não o fizéssemos teríamos obtido um erro, pois não podemos multiplicar uma matriz 1 × N por outra matriz 1 × N. Mas podemos multiplicar uma matriz 1 × N por uma matriz N × 1. Lembre que [a b c] × transposição[d e f] = ad + be + cf, um único valor. Não é preciso encontrar a soma da multiplicação ponto a ponto com o comando sum( ), pois isso é feito automaticamente. Poderíamos obter o mesmo resultado com: sum(x.*y) Para tornar claras essas expressões equivalentes, considere a correlação de dois sinais x = {x0, x1, x2, x3} e y = {y0, y1, y2, y3}. Primeiro, invertemos a ordem de y, que rotulamos de r. Portanto, r = {r0, r1, r2, r3} = {y3, y2, y1, y0}. Em seguida, encontramos a convolução de x e r. Pressupomos que os valores de x são reais; caso contrário utilizaríamos seus conjugados complexos: amostra de convolução amostra de convolução [n] = x 0r n−0 + x 1r n−1 + x 2r n−2 + x 3r n−3 Considere n = 3. amostra de convolução [3] = x 0r 3−0 + x 1r 3−1 + x 2r 3−2 + x 3r 3−3 amostra de convolução [3] = x 0r 3 + x 1r 2 + x 2r 1 + x 3r 0

Weeks 003.indd 92

04/04/12 18:54

Filtros

93

Em seguida, substitua os valores r por seus equivalentes y. amostra de convolução [3] = x 0y 0 + x 1y 1 + x 2y 2 + x 3y 3 Agora considere o resultado para sum(x .* y) diretamente. A operação .* efetua uma multiplicação ponto a ponto, resultando em x0y0 + x1y1 + x2y2 + x3y3. Constatamos que esse resultado é igual ao anterior, o que nos permite utilizar qualquer um dos métodos. Considere também x*y.', em que y.' consiste na transposição de y.

Novamente, percebemos o mesmo padrão. A escolha de n = 3 é arbitrária? Considere a hipótese de termos escolhido n = 2. Teríamos então x0r2 + x1r1 + x2r0 + x3r−1. Se partirmos do pressuposto de que os valores r se repetem, r−1 é igual a r3. Desse modo, o resultado final é x0y1 + x1y2 + x2y3 + x3y0, que equivale a uma versão deslocada de y. Como você verá em breve, deslocamos y para cada possibilidade. Em outras palavras, também utilizamos n = 2 no cálculo da correlação, assim como todos os demais valores para n.

3.8.2

Covariância Cruzada como um Exemplo Simples de Correlação

Temos aqui um exemplo numérico no qual comparamos dois sinais. Estamos construindo a correlação. O que encontramos aqui é uma aproximação da correlação cruzada. Encontramos a convolução de x e um y invertido e dividimos o resultado pela extensão desses sinais. Em seguida, dividimos pelo número de amostras. Esse código só funciona porque fomos cuidadosos em manter x e y com exatamente a mesma extensão e porque x e y por acaso possuem média zero. O MATLAB disponibiliza diversas funções para a correlação, tais como xcorr para correlação cruzada e xcov para covariância cruzada. (Para utilizar essas funções, você deve dispor da caixa de ferramentas de processamento de sinais.) As funções do MATLAB aparentemente não dividem o número de amostras por padrão. Em outras palavras, os exemplos a seguir aparentam ser a décima parte do que a função xcov retorna, a menos que você especifique o parâmetro biased. Como tais valores são relativos em magnitude, esse detalhe não é importante. >> x = [ 0 0 1 5 1 -2 -3 -2 0 0 ]; >> y = [ 1 5 1 -2 -3 -2 0 0 0 0 ]; >> conv(x,y(length(y):-1:1)) / length(x) ans = Columns 1 through 5 0

0

0

0

0

-1.3000

-1.9000

-0.8000

Columns 6 through 10 0

Weeks 003.indd 93

-0.2000

04/04/12 18:54

94

CAPÍTULO 3

Columns 11 through 15 2.0000

4.4000

2.0000

-0.8000

0

0

-1.9000

Columns 16 through 19 -1.3000

-0.2000

Note como a resposta variou de −1.9 a 4.4. Isso depende do alinhamento dos dois sinais. Leve isso em consideração. No exemplo a seguir temos a convolução de x e y (invertido), com cada linha mostrando x repetido e multiplicado por um valor y invertido. A função de convolução retorna a soma de cada coluna. Aqui, por conveniência, mostramos somente a soma da coluna com o valor maior, sem dividirmos por length(x).* 0 0 1 5 1 -2 -3 -2 0 0 0 0 0 0 -2 -3 -2 1 5 1 -------------------------------(0 0 1 5 1 -2 -3 -2 0 0) * 0 (0 0 1 5 1 -2 -3 -2 0 0) * 0 (0 0 1 5 1 -2 -3 -2 0 0) * 0 (0 0 1 5 1 -2 -3 -2 0 0) * 0 (0 0 1 5 1 -2 -3 -2 0 0) * -2 (0 0 1 5 1 -2 -3 -2 0 0) * -3 (0 0 1 5 1 -2 -3 -2 0 0) * -2 (0 0 1 5 1 -2 -3 -2 0 0) * 1 (0 0 1 5 1 -2 -3 -2 0 0) * 5 (0 0 1 5 1 -2 -3 -2 0 0) * 1 -------------------------------------------------------------44 Os resultados mostram que o maior valor de magnitude (o mais distante de 0) ocorre na coluna 12, onde a soma é (−2) × (−2) + (−3) × (−3) + (−2) × (−2) + (1) × (1) + (5) × (5) + (1) × (1). De certa forma, a convolução alinhou os dois sinais. Tal como mostra a saída anterior, o valor máximo ocorreu quando os dois sinais estavam perfeitamente alinhados. Uma pequena mudança em um dos sinais ainda resultaria no mesmo alinhamento. Aqui, redefinimos x com um zero no lugar de um dos “1”. >> x = [ 0 0 1 5 0 -2 -3 -2 0 0 ]; >> conv(x,y(length(y):-1:1))/length(x) ans = Columns 1 through 5 0

0

0

0

0

-1.3000

-1.7000

-0.5000

-0.9000

-1.9000

Columns 6 through 10 0

-0.2000

Columns 11 through 15 2.2000

4.3000

1.5000

* No caso, a extensão. (N.T.)

Weeks 003.indd 94

04/04/12 18:54

Filtros

95

Columns 16 through 19 -1.3000

-0.2000

0

0

Observe que ainda obtemos o maior valor na coluna 12. Uma leve alteração em um dos sinais resulta em um valor de correlação mais baixo – afinal de contas, os sinais são um pouco menos parecidos. Porém, se tivéssemos que alinhá-los manualmente, ainda o faríamos dessa maneira.

3.8.3

Exemplos de Correlação

A seguir, apresentamos outro exemplo. Neste, mantemos o x original, mas utilizamos uma versão negativa de y. Note que os valores gerados por nossa função de correlação simples são os mesmos que os anteriores, exceto pelo fato de terem sido negativados. Novamente, o maior valor em magnitude (−4.4) ocorre quando os sinais estão alinhados, na coluna 12. >> >> >> >>

clear all x = [ 0 0 1 5 1 -2 -3 -2 0 0 ]; y = [ 1 5 1 -2 -3 -2 0 0 0 0 ]; yn = -y

yn = -1

-5

-1

2

3

2

0

0

0

0

>> conv(x,yn(length(yn):-1:1))/length(x) ans = Columns 1 through 5 0

0

0

0

0

Columns 6 through 10 0

0.2000

1.3000

1.9000

0.8000

-2.0000

0.8000

1.9000

Columns 11 through 15 -2.0000

-4.4000

Columns 16 through 19 1.3000

0.2000

0

0

Tecnicamente, o valor retornado não é a correlação, mas a covariância cruzada. O problema é que o método empregado aqui só funciona quando o sinal possui média 0. Outro problema com esse valor é que ele não nos informa se os dois sinais correspondem perfeitamente. Ou seja, no primeiro exemplo, o valor da covariância cruzada entre x e y foi 4.4, mas, se tivéssemos que comparar o sinal x com algum outro sinal, a covariância cruzada poderia ser mais alta? Na realidade, a resposta é sim! Os dois comandos a seguir mostram por que esse é o caso. Se tomarmos o sinal y e multiplicarmos cada valor por 10, o cálculo da covariância cruzada nos levará a uma resposta dez vezes maior que a original. >> >> >> >>

Weeks 003.indd 95

clear x = [ y = [ y10 =

all 0 0 1 5 1 -2 -3 -2 0 0 ]; 1 5 1 -2 -3 -2 0 0 0 0 ]; y*10

04/04/12 18:54

96

CAPÍTULO 3

y10 = 10

50

10

-20

-30

-20

0

0

0

0

-8

>> conv(x,y10(length(y10):-1:1))/length(x) ans = Columns 1 through 10 0

0

0

0

0

0

-2

-13

-19

-19

-13

-2

0

0

Columns 11 through 19 20

44

20

-8

Neste exemplo adicional, vamos considerar um caso mais extremo. Suponha que utilizemos sinais não muito parecidos – digamos, o x que acabamos de usar versus uma função impulso (chamada yimpulse). >> x = [ 0 0 1 5 1 -2 -3 -2 0 0 ]; >> yimpulse = [ 0 0 0 0 400 0 0 0 0 0 ]; >> conv(x,yimpulse(length(yimpulse):-1:1))/length(x) ans = Columns 1 through 10 0

0

0

0

0

0

0

40

200

0

0

0

0

0

40

Columns 11 through 19 -80

-120

-80

0

Vemos que o maior valor retornado foi 200. A partir daí, poderíamos concluir erroneamente que x é o que melhor corresponde a yimpulse dentre todos os demais sinais vistos até aqui nesta seção, por ser o que gera o maior valor! Trata-se, naturalmente, de uma conclusão precipitada, pois o y original era uma cópia deslocada de x.

3.8.4

Correlação Cruzada

Para resolver o problema anterior, encontramos a correlação cruzada. Ela utiliza duas informações a mais: a autocovariância para cada sinal. No código, elas são as variáveis sxx para sx,x e syy para sy,y. A variável sxy (para sx,y), a covariância cruzada, é muito semelhante aos exemplos anteriores, exceto que nenhuma pressuposição é feita em relação às médias do sinal. Substituímos a divisão pela extensão por alguma coisa baseada nela que nos forneça resultados mais consistentes. Em vez de um valor que poderia ser muito grande (e aberto à possibilidade de não ser uma correspondência verdadeira), a correlação cruzada resulta em um valor entre −1 e +1. Implementamos sxx e syy da seguinte maneira: N = sxx syy sxy

Weeks 003.indd 96

length(x); = x*x.' - sum(x)*sum(x)/N; = y*y.' - sum(y)*sum(y)/N; = x*y.' - sum(x)*sum(y)/N;

04/04/12 18:54

Filtros

97

Tenha em mente que a multiplicação de uma lista por uma lista transposta produz o mesmo efeito do cálculo da soma de duas listas multiplicadas entre si ponto a ponto. Em vez de calcular a correlação para todos os sinais de uma vez (ou seja, com a convolução), esse método encontra um único coeficiente de correlação. Observe também que sxx e syy chegam ao mesmo valor ainda que as listas sejam deslocadas. Ou seja, x*x.' resulta em (x0 × x0 + x1 × x1 + x2 × x2 + x3 × x3). Se reorganizássemos x – por exemplo, como {x2, x0, x3, x1} –, o resultado da multiplicação da matriz (de x por ele mesmo, só que transposto) seria (x2 × x2 + x0 × x0 + x3 × x3 + x1 × x1), exatamente o mesmo valor de antes. Da mesma forma, a soma não mudará se reorganizarmos a lista; portanto sxx chegará ao mesmo valor independentemente de utilizarmos o x original ou o reorganizado. O mesmo se aplica a syy, porém não é válido para sxy, pois a reorganização de x significaria que diferentes valores de x seriam multiplicados por diferentes valores de y. Em seguida, encontramos o valor da correlação cruzada, rho. rho = sxy / sqrt(sxx*syy); Ignoraremos por enquanto o deslocamento do sinal y e simplesmente consideraremos que ele está corretamente alinhado ao sinal x. Agora, examinaremos alguns exemplos. Primeiramente, definimos nossos sinais. Utilizamos os mesmos nomes de antes, embora os elementos da lista não estejam nos mesmos lugares. Em vez de ser uma cópia deslocada, y é exatamente igual a x. x = [ 0 0 1 5 1 -2 -3 -2 0 0 ]; y = [ 0 0 1 5 1 -2 -3 -2 0 0 ]; yn = -y; y10 = y*10; yimpulse = [ 0 0 0 0 400 0 0 0 0 0 ]; N = length(x); Em seguida, calculamos sxx. >> sxx = x*x.' - sum(x)*sum(x)/N sxx = 44 Da mesma forma, calculamos syy e variáveis correlatas. >> syy = y*y.' - sum(y)*sum(y)/N syy = 44 >> syyn = yn*yn.' - sum(yn)*sum(yn)/N syyn = 44 >> syy10 = y10*y10.' - sum(y10)*sum(y10)/N

Weeks 003.indd 97

04/04/12 18:54

98

CAPÍTULO 3

syy10 = 4400 >> syyimpulse = yimpulse*yimpulse.' - sum(yimpulse)*sum(yimpulse)/N syyimpulse = 144000 Com esses valores definidos, podemos agora computar os valores de covariância cruzada (sxy) correspondentes. >> sxy = x*y.' - sum(x)*sum(y)/N sxy = 44 >> sxyn = x*yn.' - sum(x)*sum(yn)/N sxyn = -44 >> sxy10 = x*y10.' - sum(x)*sum(y10)/N sxy10 = 440 >> sxyimpulse = x*yimpulse.' - sum(x)*sum(yimpulse)/N sxyimpulse = 400 Enfim, podemos calcular os valores de correlação cruzada (rho). >> rho = sxy/sqrt(sxx*syy) rho = 1 >> rhon = sxyn/sqrt(sxx*syyn) rhon = -1 >> rho10 = sxy10/sqrt(sxx*syy10) rho10 = 1 >> rhoimpulse = sxyimpulse/sqrt(sxx*syyimpulse) rhoimpulse = 0.1589

Weeks 003.indd 98

04/04/12 18:54

Filtros

99

O aspecto interessante aqui é que descobrimos o impostor. O único sinal que claramente não corresponde é o sinal de impulso, conforme rhoimpulse indica. Obtivemos valores de correlação cruzada iguais a +1 para rho e rho10, como esperávamos, visto que y é uma cópia e y10 é uma cópia escalonada por 10. Assim como uma pessoa pode dizer “eles parecem iguais” ou “eles parecem iguais, só que um é maior”, nossos valores de correlação cruzada nos informam que esses dois sinais correspondem perfeitamente ao sinal original (x). Em −1, rhon indica uma forte correlação negativa entre x e yn. Isso também é de se esperar, já que yn é uma versão negativa de y. Em resumo, constatamos que a correlação cruzada nos fornece melhores informações sobre o nível de correspondência entre dois sinais. Sua execução requer alguns cálculos a mais, tais como as autocovariâncias (sxx e syy) e a covariância cruzada (sxy), mas o resultado possui os limites −1 e +1, com isso nos informando sobre a correspondência em relação a outras correspondências em potencial.

3.8.5

Acerca do Deslocamento de um Sinal

Vimos como a convolução pode encontrar valores de correlação e como pode alinhar uma lista com outra de forma a fornecer um resultado máximo. Mas o que aconteceria se considerássemos sinais como os exibidos na Figura 3.30? Percebemos imediatamente que os dois sinais têm a mesma aparência, exceto pelo fato de um parecer uma versão deslocada do outro. Se pudéssemos simplesmente mover a linha tracejada um pouco para a direita, conseguiríamos alinhá-los perfeitamente. Mas o que faríamos a respeito das amostras inicial e final? Existem duas possibilidades viáveis. Poderíamos considerar que todos os valores precedentes e subsequentes são iguais a zero, ou que os sinais se repetem. Essa segunda hipótese parece a mais razoável, pois de outro modo os sinais apresentariam grandes descontinuidades. Vamos examinar o funcionamento de ambos os métodos. Primeiramente, geramos os sinais mostrados no exemplo. Utilizamos t para um tempo simulado e x e y para os dois sinais a comparar. O código a seguir inclui um traçado, para que possamos visualizar esses dois sinais. t = 0:0.001:0.43; x = cos(2*pi*t*7);

1 0.8 0.6

Amplitude

0.4 0.2 0 – 0.2 – 0.4 – 0.6 – 0.8 50 Figura 3.30

Weeks 003.indd 99

100

150

200 250 300 Número da amostra

350

400

Dois sinais de exemplo, sendo que um parece ser uma versão deslocada do outro.

04/04/12 18:54

100

CAPÍTULO 3

y = cos(2*pi*t*7 + pi/4); plot(1:length(x), x, 'r', 1:length(y), y, 'm-.'); Visualmente, percebemos na Figura 3.30 que ambos possuem valores de pico entre as amostras 100 e 150. Mas onde eles estão exatamente? >> [val, x_peak] = max(x(100:150)) val = 1.0000

x_peak = 45 >> [val, y_peak] = max(y(100:150)) val = 1

y_peak = 27 A partir dos resultados, concluímos que o máximo (x_peak) ocorre no sinal x na amostra 45 a partir da lista de amostras começando em 100. Em outras palavras, a amostra 100 é o primeiro item da sublista. A amostra 101 é o segundo item, 102 o terceiro item etc. Portanto, o 45º item corresponde efetivamente ao número de amostra 144. Da mesma forma, o máximo para y, dentro do intervalo 100:150 (y_peak), por acaso se encontra na amostra 126. A diferença, 144 − 126 = 18, nos informa o quanto o sinal y deve ser movido para se alinhar com x. Agora criaremos dois sinais novos. A lista y2 gera uma cópia de y, com 18 zeros inclusos no lado esquerdo. Do mesmo modo, a lista x2 gera uma cópia de x e insere 18 zeros à direita. Assim, as duas listas (x2 e y2) possuem a mesma extensão. y2 = [zeros(1,18), y]; x2 = [x, zeros(1,18)]; Se as traçarmos, veremos que estão sobrepostas, como na Figura 3.31. Sua correlação nesse ponto deve estar bastante próxima de 1. Agora calcularemos a correlação cruzada nesse ponto. (Precisaremos recalcular sxx e syy, pois alteramos as extensões das listas.) >> >> >> >> >>

N2 = length(x2); sxx = x2*x2.' - sum(x2)*sum(x2)/N2; syy = y2*y2.' - sum(y2)*sum(y2)/N2; sxy = x2*y2.' - sum(x2)*sum(y2)/N2; rho = sxy/sqrt(sxx*syy)

rho = 0.9337

Weeks 003.indd 100

04/04/12 18:54

Filtros

101

1 0.8 0.6

Amplitude

0.4 0.2 0 – 0.2 – 0.4 – 0.6 – 0.8 50

100

150

200 250 300 Número da amostra

350

400

Figura 3.31 Dois sinais de exemplo, alinhados.

Alternativamente, poderíamos adotar outro procedimento. Observe que não executamos qualquer deslocamento em y, mas utilizamos a convolução para encontrar sxy, escolhendo em seguida o valor máximo a partir da lista de resultados. Por conveniência, repetimos as definições de t, x e y. >> >> >> >> >> >> >> >>

t = x = y = N = sxx syy sxy rho

0:0.001:0.43; cos(2*pi*t*7); cos(2*pi*t*7 + pi/4); length(x); = x*x.' - sum(x)*sum(x)/N; = y*y.' - sum(y)*sum(y)/N; = conv(x, y(length(y):-1:1)) - sum(x)*sum(y)/N; = max(sxy) / sqrt(sxx*syy)

rho = 0.9376 O código anterior mostra um modo fácil de obter uma boa aproximação do coeficiente de correlação, sem que seja preciso deslocar um dos sinais. Nosso valor final, rho, também indica uma forte correlação entre os dois sinais. Escolhemos o valor máximo para isso, pois sabíamos antecipadamente que o coeficiente de correlação seria positivo. Normalmente, precisaríamos optar entre o valor mais positivo e o mais negativo. O coeficiente de correlação cruzada mostra que os dois sinais são muito semelhantes. E se considerarmos os dados como circulares? Em vez de inserirmos amostras nulas, truncaremos a lista y e deslocaremos 18 amostras do fim para o início. k = length(y) - 18; y3 = [y(k+1:length(y)), y(1:k)]; Nenhum traçado é mostrado aqui, pois a segunda curva não é visível por trás da primeira. Agora compararemos essa versão de y com o x original.

Weeks 003.indd 101

04/04/12 18:54

102

CAPÍTULO 3

>> >> >> >> >>

N = sxx syy sxy rho

length(x); = x*x.' - sum(x)*sum(x)/N; = y3*y3.' - sum(y3)*sum(y3)/N2; = x*y3.' - sum(x)*sum(y3)/N2; = sxy/sqrt(sxx*syy)

rho = 0.9999 O valor da correlação cruzada é muito próximo de 1, indicando uma correspondência quase perfeita. Com essa técnica de deslocamento circular da lista y, podemos escrever uma função de correlação que funciona com qualquer deslocamento que quisermos. Caso desejássemos, poderíamos tentar muitos valores de deslocamento diferentes até descobrirmos aquele que nos fornecesse o resultado máximo (ou mínimo).

3.8.6

Um Programa de Correlação Cruzada

Existem duas maneiras de lidar com nossos dados para calcularmos a correlação. Uma maneira simples consiste em utilizarmos a convolução e tomarmos o valor de maior magnitude. Porém, se possuirmos dados circulares, fará mais sentido executarmos um deslocamento circular. Em um exemplo posterior (ainda nesta seção), compararemos dois sinais com dados circulares. Os sinais procedem de medições entre o centroide* e as bordas de uma figura geométrica. Portanto, a primeira amostra é arbitrária e sua predecessora à esquerda é a última amostra. O código a seguir calcula a correlação cruzada entre dois sinais. Dado um parâmetro k, o código desloca o segundo sinal e retorna o valor da correlação cruzada para aquele deslocamento. Para encontrarmos o maior valor de magnitude, precisamos chamar essa função com todos os valores de deslocamento possíveis. % % correlate.m – O quanto dois sinais sao semelhantes? % % Uso: [rho] = correlate(k, x, y); % entradas: k = deslocamento (inteiro entre 0 e length(y)–1) % x, y = os sinais a serem correlacionados % saida: rho = o coeficiente de correlacao –1..0..1 % % Nota: Esta funcao considera length(x) = length(y). % function [rho] = correlate(k, x, orig_y) % Desloque y por k unidades y = [orig_y(k+1:length(orig_y)), orig_y(1:k)]; N = length(x); sxx = x*x.' - sum(x)*sum(x)/N; syy = y*y.' - sum(y)*sum(y)/N; sxy = x*y.' - sum(x)*sum(y)/N; rho = sxy / sqrt(sxx*syy);

* Centro geométrico. (N.T.)

Weeks 003.indd 102

04/04/12 18:54

Filtros

103

Ao rodarmos o código, constatamos que a saída rho (␳), a correlação cruzada, é um bom valor para se utilizar se comparado a técnicas mais simples, por nos fornecer uma indicação (até 1) de quanto os dois sinais se parecem. >> >> >> >> >> >> >>

clear all x = [ 0 0 1 5 1 -2 -3 -2 0 0 ]; y = [ 1 5 1 -2 -3 -2 0 0 0 0 ]; yn = -y; y10 = y*10; yimpulse = [ 0 0 0 0 400 0 0 0 0 0 ]; rho = correlate(8, x, y)

rho = 1 >> rho = correlate(8, x, y10) rho = 1 >> rho = correlate(8, x, yn) rho = -1 >> rho = correlate(8, x, yimpulse) rho = -0.4767 O último exemplo mostra que existe uma correlação negativa entre x e yn (a versão negativa de y). A função de correlação cruzada lida com o fato de que os sinais possam ser versões escalonadas um do outro. Ou seja, descobrimos que x e y10 eram equivalentes, muito embora os valores de y10 fossem dez vezes os de x. Vamos examinar outro exemplo. O que aconteceria se tentássemos correlacionar x a um sinal diferente dele? Chamaremos o segundo sinal de noise (ruído). >> >> >> >>

clear all x = [ 0 0 1 5 1 -2 -3 -2 0 0 ]; noise = [ 0 -1 0 1 0 -1 1 0 1 -1]; for i=0:length(x) my_rho(i+1) = correlate(i, x, noise);

end >> my_rho my_rho = Columns 1 through 6 0.2462

-0.2462

-0.3077

0.3077

0.3693

-0.3077

0.0615

0.2462

0.4308

Columns 7 through 11 -0.2462

Weeks 003.indd 103

-0.3077

04/04/12 18:54

104

CAPÍTULO 3

>> min_rho=min(my_rho); max_rho=max(my_rho); >> if (abs(min_rho) > abs(max_rho)) min_rho else max_rho end max_rho = 0.4308 A lista my_rho armazena todos os coeficientes de correlação para esses dois sinais. Para verificarmos o nível de semelhança entre x e noise, precisamos examinar tanto o valor mínimo quanto o máximo. Com a correlação cruzada, somos capazes de apontar se dois sinais se parecem ou não.

Exemplo 3.9 O exemplo a seguir deriva de um projeto de processamento de imagem. A Figura 3.32 exibe dois retângulos. Nosso objetivo é correlacionar automaticamente esses dois objetos – algo que as pessoas podem fazer facilmente, mas que, para um computador, é um processo difícil de comparação de padrão. Problemas como este, de fácil solução para uma pessoa, porém difíceis para um computador, constituem alguns dos desafios mais interessantes.

Figura 3.32

Dois retângulos de tamanho (escala) e rotação diferentes.

Dois retângulos (normalizados) como distâncias unidimensionais a partir do centro – linha sólida para o esquerdo, pontilhada para o direito 70 65 60 55 50 45 40 35 30 50 Figura 3.33

Weeks 003.indd 104

100

150

200

250

300

350

400

Dois retângulos representados como a distância a partir de seus centros até suas bordas.

04/04/12 18:54

Filtros

105

Os retângulos são semelhantes, embora um seja menor do que o outro e tenha sido girado (ou seja, um é mais longo do que largo, enquanto o outro é mais largo do que longo). Para que o computador determine que eles se equivalem, rastrearemos os perímetros desses objetos e calcularemos o ponto central. A partir desse ponto, mediremos a distância para cada ponto que compõe o perímetro, começando pelos vértices superiores esquerdos e percorrendo o objeto no sentido horário. Quando terminarmos, teremos uma visão unidimensional desses objetos, conforme visto na Figura 3.33. A linha sólida representa o objeto à esquerda na Figura 3.32, enquanto a linha pontilhada representa o objeto à direita. Observe que ambas possuem a mesma extensão. Isso é necessário para a correlação, de modo que a menor das duas foi “esticada” para ficar mais longa. Os sinais originais são d1 e d2, e o código a seguir mostra um modo simples de esticar o sinal mais curto d2 para que ele tenha a mesma extensão que d1. A primeira linha cria uma lista de pontos em incrementos fracionários. A segunda linha gera um novo sinal de dados chamado d3 que é uma cópia de d2, com pontos redundantes. Existem modos melhores de interpolar um sinal (consulte o comando spline do MATLAB), mas esse método é satisfatório para este exemplo. pts = 1:length(d2)/length(d1):length(d2)+1; d3 = d2(round(pts(1:length(d1)))); Uma vez que já dispomos dos dois sinais de mesma extensão para comparar, podemos utilizar a função de correlação e tomar o maior valor como uma indicação do nível de semelhança entre os objetos. >> for k=1:length(d1) rhos(k) = correlate(k, d1, d3); end >> disp(sprintf('max. coeficiente de correlacao: max. coeficiente de correlacao: 0.9994

%5.4f', max(rhos)));

Nosso código nos fornece um coeficiente de correlação 0.9994, indicando uma equivalência muito boa entre os dois objetos, conforme esperado.

Exemplo 3.10 Para provarmos que o exemplo anterior não foi apenas uma coincidência, repetiremos o experimento com um retângulo e um triângulo (veja a Figura 3.34).

Figura 3.34 Um retângulo e um triângulo.

Desta vez, constatamos que os sinais unidimensionais resultantes são diferentes, como poderíamos esperar (veja a Figura 3.35). Com o retângulo, vemos o sinal unidimensional tornando-se gradualmente menor e depois novamente maior até alcançar a mesma altura. Em seguida, ele fica um pouco menor e retorna à altura original. Esse padrão se repete. A partir do centro, os vértices irão se tornar os pontos mais distantes, e a representação disso será a maior altura. À medida que rastreamos o perímetro do retângulo, a distância para o centro torna-se um pouco menor e depois novamente maior até alcançarmos o próximo vértice. Enquanto rastreamos o lado seguinte, a distância torna-se menor e depois retorna à distância original no vértice. Visto que o segundo lado não é tão longo quanto o primeiro, a redução gradual em altura não é tão acentuada. Com o triângulo, os vértices não são equidistantes do centro

Weeks 003.indd 105

04/04/12 18:54

106

CAPÍTULO 3

Dois objetos (normalizados) como distâncias unidimensionais a partir do centro – linha sólida para o esquerdo, pontilhada para o direito 70

60

50

40

30

20

50 Figura 3.35 bordas.

100

150

200

250

300

350

400

Um retângulo e um triângulo representados como a distância a partir de seus centros até suas

(devido ao uso de uma simples função de cálculo de média para encontrar o centro); portanto vemos três picos na representação unidimensional do objeto em linha pontilhada, mas dois deles não estão tão distantes do centro quanto o terceiro. Começamos do vértice superior direito, embora a posição inicial não seja importante pelo fato de os cálculos de correlação levarem em consideração o deslocamento do sinal. max correlation coeff: 0.6272 Quando calculamos os coeficientes de correlação, encontramos um valor máximo de apenas 0.6272. Podemos concluir que esses objetos apresentam alguma semelhança, mas estão longe de ser equivalentes. O site da LTC Editora contém três programas que alinham os dois retângulos do exemplo mostrado anteriormente. São eles: match_rectangle1.m, match_rectangle2.m e match_rectangle3.m.

RESUMO . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Os filtros de resposta finita ao impulso (FIR) são apresentados neste capítulo, incluindo sua estrutura e comportamento. Três propriedades importantes que os filtros FIR possuem são a causalidade, a linearidade e a invariância no tempo. Pelo fato de a filtragem exercer um papel tão importante, os filtros FIR são os burros de carga do processamento digital de sinais. Os filtros de resposta infinita ao impulso (IIR) também constituem um tópico deste capítulo. Também estudamos a correlação, que pode ser feita com convolução. Como os filtros FIR executam a convolução, discutimos esses dois conceitos no mesmo lugar. A correlação retorna muitos coeficientes diferentes, com base no modo como um sinal é deslocado em relação ao outro. O coeficiente de correlação com a maior magnitude (negativa ou positiva) corresponde à melhor correlação. Portanto, embora uma função de correlação possa retornar um único valor baseado em um deslocamento (correlate.m) ou possa retornar muitos valores (tal como o comando xcorr do MATLAB), normalmente estamos interessados em apenas um coeficiente de correlação escolhido a partir dessas saídas.

Weeks 003.indd 106

04/04/12 18:54

Filtros

107

EXERCÍCIOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1. FIR é um acrônimo de que expressão? O que ele significa? 2. O que significa causal? 3. O que significa linear? 4. O que significa invariante no tempo? 5. Escreva um código MATLAB para computar a saída (y[n]) para convolução, dados quaisquer coeficientes de filtro FIR (b[k]) e entrada (x[n]). 6. Filtre a entrada x = [5, 1, 8, 0, 2, 4, 6, 7] com um filtro FIR de 2 taps de coeficientes {1, −1} manualmente (não utilize um computador). 7. Que palavra descreve a operação a seguir? __________ de x[n] e b[n] produz y[n].

8. Crie um sinal de teste de no mínimo 10 valores e filtre-o com os coeficientes a seguir. Utilize o MATLAB para este exercício. (a) b[k] = {0.4, 0.4} (b) b[k] = {0.2, 0.2, 0.2, 0.2} (c) b[k] = {0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1} O que você observa? Qual conjunto de valores de filtro cumpre melhor a tarefa de “suavizar” o sinal? 9. O que chamamos de h[n]? Por que ele é significativo? 10. MAC é um acrônimo de que expressão? O que ele significa? 11. Em relação ao sistema1 e ao sistema2 a seguir, trata-se de sistemas lineares? São eles invariantes no tempo? São causais? Explique. Considere x[n] a entrada e y[n] a saída, onde sistema1 é y1[n] = x[n] − x[n − 1] e sistema2 é y2[n] = 3x[n] + 1. 12. Qual é a diferença entre um filtro FIR e um filtro IIR? Desenhe um de cada. 13. Suponha que tenhamos um sistema onde y[n] = 2x[n] + x[n − 1]. (a) Esse sistema é causal? (b) Esse sistema é linear? (c) Esse sistema é invariante no tempo? 14. Para o sinal de entrada x[n] = [6, 1, 3, 5, 1, 4] e um filtro h[k] = [−1, 3, −1], encontre y[n], em que y[n] = h[k] * x[n]. Utilize o algoritmo fornecido na Seção 3.2. 15. Escreva uma função MATLAB para calcular a saída de um filtro IIR. Considere que o filtro possui quatro coeficientes de alimentação direta e três coeficientes de realimentação, definidos no topo de sua função. 16. Escreva uma função MATLAB para testar se um sistema é independente de tempo. Considere que o sistema é definido em uma função externa – por exemplo, system1.m –, que ele toma uma lista como entrada e que ele gera uma lista como saída.

Weeks 003.indd 107

04/04/12 18:54

108

CAPÍTULO 3

PROJETO . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Dado um sinal que mede a distância do centroide até o perímetro de uma forma geométrica, utilize a correlação para encontrar a equivalência mais próxima entre ela e outras formas geométricas. Um exemplo de solução é fornecido no site da LTC Editora, como project3.m. Tente resolver esse problema primeiro por conta própria e depois leia o texto a seguir.

Para responder a essa questão, precisamos de um sinal de exemplo, bem como algumas formas para comparação. Quando comparamos formas anteriormente neste capítulo, nós as representamos como um sinal unidimensional medindo a distância até o perímetro a partir do centro. Faremos o mesmo aqui. Naturalmente não existe uma resposta certa ou errada para o sinal de exemplo. Na realidade, um bom teste precisaria de no mínimo dois sinais de exemplo. Um deveria ser muito semelhante a uma forma, ao contrário do outro. Começamos definindo um vértice e copiando-o em seguida quatro ou cinco vezes para representar um quadrado ou uma estrela, respectivamente. point = [14:0.2:20, 20:-0.2:14]; signal = [point, point, point, point]; % quadrado % sinal = [point, point, point, point, point]; % estrela Insira um sinal de comentário na segunda linha e remova-o na terceira para comutar entre os sinais. Em seguida, precisamos de algumas formas-padrão para comparar nosso sinal. Definimos primeiramente um retângulo, especificando dois pontos correspondentes aos vértices superior esquerdo e inferior direito. Depois, encontramos o centro calculando a média desses pontos. A partir do centro até cada posição ao longo do lado superior do retângulo, registramos as medidas. Como se trata de um retângulo, mantemos a linha fixa (valor do eixo y) e deixamos a coluna (valor do eixo x) variar a partir do valor de x do primeiro ponto até aquele do segundo ponto. Da mesma forma, encontramos a seguir as medidas do lado direito mantendo a coluna fixa enquanto calculamos cada ponto do perímetro para as linhas. Repetimos esse procedimento novamente para a base do retângulo, indo da coluna máxima até a coluna mínima, e em seguida rastreamos o lado esquerdo até alcançarmos o ponto inicial. Desse modo, criamos o retângulo como um sinal unidimensional. Depois, podemos criar um triângulo de modo muito semelhante. Na realidade, copiamos o código para o retângulo e o reutilizamos, pois algumas das ideias ainda são válidas. Ou seja, o lado direito e a base do triângulo podem ser encontrados exatamente da mesma forma como encontramos esses dois lados do retângulo. O único desafio real é encontrar as medidas ao longo da diagonal, a partir do canto inferior esquerdo até o canto superior direito. O código encontrará seu caminho ao longo da diagonal, subindo e depois indo para a direita ou subindo e indo para a direita simultaneamente. Como decidimos qual caminho tomar? Para tornarmos nosso programa mais fácil, carregamos a imagem rect_triangle.ppm e a utilizamos como um guia. Com o conhecimento prévio da posição do vértice inferior direito, examinamos o pixel na linha acima, em seguida o pixel no canto superior direito e depois o pixel à direita. Escolha o primeiro que contém um pixel preto. Se nenhum deles contiver, exiba uma mensagem de erro e interrompa o programa. Paramos quando alcançamos o vértice superior direito. O sinal unidimensional que medimos a partir do centro nos fornece o sinal do triângulo. Seria interessante criar algumas outras formas como teste, mas temos o bastante para fazer uma comparação. O próximo desafio é normalizar nossos sinais. Cada um deles possui uma extensão diferente, então como compará-los? Mesmo um simples somatório de diferenças não funcionará até que cada um deles tenha a mesma extensão. Assim, descobrimos qual é o maior e escalonamos cada um dos sinais até aquela extensão. O código a seguir cumpre essa tarefa para o sinal de exemplo. norm_len = ... max([length(signal) length(rectangle) length(triangle)]); pts = round((1:norm_len) * (length(signal)-1)/norm_len); signal_norm = signal(round(pts+1));

Weeks 003.indd 108

04/04/12 18:54

Filtros

109

A primeira linha deve ser autoexplicativa: através dela encontramos a extensão normalizada como a extensão máxima de signal, rectangle e triangle. Em seguida, criamos uma lista denominada pts (pontos) que imita uma linha numérica. Se a extensão de normalização for a mesma que a extensão do sinal, a lista pts consistirá nos números que vão de um até a extensão menos um. Mas, quando as extensões forem diferentes, pts amostrará novamente o sinal para esticá-lo. Ou seja, signal(round(pts+1)) forma uma nova lista com alguns valores de signal repetidos. Chamamos esse novo sinal de signal_norm, já que o programa normaliza sua extensão. O programa gera versões normalizadas dos outros dois sinais da mesma maneira. Enfim, estamos prontos para comparar os sinais. Se os correlacionarmos e encontrarmos os valores máximo e mínimo, poderemos então escolher o valor ␳ mais distante de zero. Isso nos informará o nível de semelhança entre os sinais. O programa projetc3.m contém essa solução. Um programa correlato, project3b.m, traça uma forma geométrica baseada no sinal simulado. Ou seja, podemos verificar visualmente nosso quadrado ou estrela. Curiosamente, o quadrado aparece como uma nuvem, com cantos arredondados. A título de exercício, ajuste a definição de point da lista para verificar como ele afeta a forma.

Weeks 003.indd 109

04/04/12 18:54

Weeks 003.indd 110

04/04/12 18:54

Senoides

4

A maioria dos sinais analógicos lembra uma combinação de funções seno/cosseno (senoides) ou, no mínimo, pode ser aproximada como uma combinação de senoides. Isso torna as combinações de senoides especialmente interessantes. É fácil somar senoides – ao pressionarmos as teclas de um piano ou produzirmos um acorde em um violão, somamos diversas senoides (embora elas efetivamente decaiam, ao contrário das senoides que habitualmente estudamos). Neste capítulo, investigaremos as senoides e veremos como podem ser somadas para modelar sinais. O objetivo é melhorar nossa compreensão sobre as senoides e nos prepararmos para o estudo da transformada de Fourier. Reproduzimos abaixo a fórmula de Euler. Ela relaciona senoides à notação exponencial. e j ␪ = cos(␪) + j sin(␪)

(4.1)

Existe também um inverso para a fórmula de Euler, como segue. (4.2) Existe uma fórmula semelhante para sin(␾), embora utilizemos principalmente a equação anterior. (4.3) Podemos utilizar essas fórmulas como uma transformada para passarmos de uma representação como e j ( ) para cos( ) + j sin( ) ou vice-versa. Mas para que fazemos isso? De onde vem e? E o que j realmente significa? Essas e outras perguntas serão respondidas neste capítulo. Ao longo do caminho, examinaremos alguns conceitos importantes sobre o DSP, incluindo como um número complexo pode ser encarado como um fasor, um número na forma de re j ␪ no plano complexo (em vez de um número real na linha numérica real), o quanto um fasor pode girar, como podemos lidar com representações alternativas da mesma informação (tais como coordenadas polares versus coordenadas cartesianas) e como algumas representações são mais apropriadas para manipulações matemáticas do que outras.

4.1

Revisão de Geometria e Trigonometria

Não há razão para se temer as letras gregas. A matemática utiliza variáveis, assim como o fazemos em programação computacional. Essas variáveis são representadas por uma letra, o que permite que as coisas sejam descritas de forma concisa. As letras gregas muitas vezes são utilizadas simplesmente porque tendemos a esgotar nossas letras alfabéticas. Além disso, existem letras que devem ser evitadas, tais como l e o, por se parecerem muito com os números 1 e 0. Considere um triângulo retângulo (veja a Figura 4.1). Graças a Pitágoras (ou no mínimo à sua escola [16]), sabemos que a2 + b2 = c2. Portanto, se por acaso conhecermos somente dois lados, poderemos calcular o terceiro.1 Além disso, com essa informação, podemos calcular os ângulos ␪1 e ␪2. Como se trata de um triângulo retângulo, o terceiro ângulo é de 90° por definição.

1

De acordo com [17], os babilônios sabiam disso, mas os gregos foram os primeiros a prová-lo.

111

Weeks 004.indd 111

04/04/12 19:07

112

CAPÍTULO 4

␪2

c

b

␪1 a Figura 4.1 Um triângulo retângulo.

A função cosseno nos fornece a relação entre o comprimento do lado adjacente a e o comprimento da hipotenusa c. Em outras palavras,

A função seno* nos fornece a relação entre o comprimento do lado oposto b e o comprimento da hipotenusa c. Em outras palavras,

As funções cos−1 e sin−1 (também chamadas de arccos e arcsin) nos fornecem o ângulo ␪1 a partir da relação entre os lados.

4.2

O Número ␲

É impossível falar a respeito de funções seno e cosseno sem mencionar a constante ␲, a relação entre o diâmetro de um círculo e sua circunferência (veja a Figura 4.2). Centralizado na origem, o círculo cruza o eixo x em +r e −r. Você provavelmente se lembra da equação: circunferência = 2␲r, em que r é o raio do círculo. Pi (␲) é um número irracional. Não importa o número de dígitos que você utilize para calcular seu valor, alguém sempre poderá acrescentar mais um. Até hoje, nenhum padrão de repetição foi encontrado e, portanto, diferentemente dos números racionais, você não pode representar ␲ em uma forma compacta. Por exemplo, com um número racional tal como 1/3, você poderia representá-lo como 0.33– para indicar que ele se repete indefinidamente quando representado como um número decimal, ou no mínimo ele pode ser representado como um número (1) dividido por outro (3).

O perímetro é igual a 2␲ r –r

+r

Figura 4.2 Um exemplo de círculo.

* Sin, no MATLAB. (N.T.)

Weeks 004.indd 112

04/04/12 19:07

Senoides

113

Os povos antigos dispunham de várias aproximações para ␲. Os egípcios utilizavam 256/81 para ␲, enquanto os babilônios calculavam com 3 1/8 [18]. Nós utilizaremos a aproximação 3.1415927 para ␲. Especificaremos componentes da senoide com o formato a × cos(2␲ ft + ␾) em que a é a amplitude, f é a frequência e ␾ é o ângulo de fase. A variável t representa o tempo. O seno pode ser utilizado no lugar do cosseno se adicionarmos ␲/2 ao ângulo de fase, pois cos(␪) = sin(␪ + ␲/2). A amplitude (a), o deslocamento de fase (␾) (também chamado de ângulo de fase) e a frequência especificam completamente uma senoide. Utilizamos f para representar a frequência cíclica, também chamada simplesmente de frequência, embora ocasionalmente a frequência em radianos* (␻) seja utilizada por conveniência. Ela relaciona f à equação ␻ = 2␲f. As unidades de f são em hertz, ciclos por segundo, homenagem a Heinrich Hertz (1857-1894), um físico alemão cujos experimentos confirmaram as teorias de onda eletromagnética de Maxwell [19]. Como a unidade Hz tem como origem um nome próprio, a primeira letra deve ser sempre maiúscula.

4.3

Círculos Unitários

Um círculo unitário é particularmente interessante. Para um círculo unitário, o raio r é igual a 1. Portanto, a circunferência – ou a extensão do círculo caso ele seja cortado e desenrolado – é igual a 2␲. Como resultado, podemos falar de um ângulo em termos da extensão do arco entre dois segmentos de linha unidos em um ponto. Chamamos esse ângulo de radiano** (rad, para encurtar). Na Figura 4.3, o ângulo ␪ é apenas uma fração da circunferência do círculo e, portanto, é uma fração de 2␲. A Figura 4.4 exibe os comprimentos de arco em um círculo unitário para diversos ângulos comuns. Sem dúvida você também está familiarizado com um ângulo medido em graus. Ao lidar com funções senoidais (tais como seno, cosseno etc.), é importante saber se a função espera que o argumento esteja em graus ou em radianos. Em uma calculadora científica, você provavelmente dispõe de um botão com o rótulo DRG para permitir a especificação de graus ou radianos, embora o padrão provavelmente seja em graus. Para o MATLAB, C/C++ e Java, considera-se que os argumentos para as funções sin( ) e cos( ) estejam em radianos.



Figura 4.3 Um ângulo especificado em radianos.

* Ou frequência angular. (N.T.) ** Ou ângulo plano. (N.T.)

Weeks 004.indd 113

04/04/12 19:07

114

CAPÍTULO 4

␲ /2



2␲

3␲ /2 Figura 4.4 Comprimentos de arco para diversos ângulos comuns.

Para a conversão entre graus e radianos, lembre-se de que uma rotação completa em torno da borda do círculo unitário equivale a 2␲ radianos, ou 360°. Para a conversão de graus em radianos, portanto, multiplicamos por 2␲/360. Para a conversão de radianos em graus, multiplicamos por 360/(2␲). Suponha, por exemplo, que o ângulo mostrado na Figura 4.3 seja de 30°,

A conversão de ␲/6 novamente em graus pode ser feita de forma semelhante:

Pergunta: O que é maior, cos(0) ou cos(1000000)? À primeira vista, alguém não familiarizado com a função cosseno pode presumir que a última é maior, simplesmente porque o argumento é maior. Porém, como as senoides se repetem, não há razão para ser esse o caso. Na verdade, alguém pode lembrar que cos(0) é 1 e que cos(qualquer número) sempre gera um valor entre +1 e −1. Assim sendo, cos(0) será maior ou igual a cos(qualquer número). Na realidade, cos(1000000) ⬇ 0.9368, certo? Se não, a discrepância provavelmente se deve ao fato de sua calculadora estar no modo graus! Como não foi especificado que o ângulo está em graus, pode-se presumir que esteja em radianos. Outro aspecto que é preciso levar em consideração ao se lidar com funções senoidais é que elas são repetitivas. O ângulo especificado por 0 radiano é o mesmo especificado por 2␲ radianos. Portanto, você sempre pode adicionar 2␲ a um ângulo ou subtrair 2␲ de um ângulo. Na verdade, você pode adicionar ou subtrair qualquer inteiro múltiplo de 2␲ de um ângulo. Às vezes isso facilita as coisas.

4.4

O Principal Valor do Deslocamento de Fase

Uma senoide prolonga-se infinitamente em ambas as direções. Se quisermos visualizá-la em termos de um eixo de tempo, poderemos utilizar os picos positivos da senoide para relacioná-la ao nosso eixo de tempo.

Weeks 004.indd 114

04/04/12 19:07

Senoides

115

Amplitude

1 0.5

0 – 0.5 –1 –0.05

–0.04

–0.03

–0.02

–0.01

0 Tempo

0.01

0.02

0.03

0.04

0.05

Figura 4.5 Uma senoide de 60 Hz.

A Figura 4.5 exibe um exemplo de senoide, cos(2␲60t), com uma frequência de 60 Hz. Portanto, o período de tempo antes que essa senoide se repita é de 1/60, ou 0.0167 segundo. Linhas verticais mostram onde uma repetição começa e termina, em t = −0.0167/2 e t = +0.0167/2. Isso corresponde aos argumentos −␲ e +␲ para função cos, ou seja, considere t = (1/60)(−1/2) para tornar cos(2␲60t) igual a cos(2␲60(1/60) (−1/2)) = cos(−␲). Mas qual dos picos normalmente utilizamos? Na Figura 4.5 é muito fácil, mas uma função senoidal frequentemente possui um ângulo de fase diferente de zero. Uma vez que ela se repete a cada 2␲, poderíamos escolher qualquer pico que quiséssemos para classificar o sinal. Isso significa que sempre haverá um pico positivo entre −␲ e +␲. Em outras palavras, −␲ < ␾ < − +␲, em que ␾ é o deslocamento de fase [7]. Assim, por que não utilizar o pico positivo mais próximo de zero? Isso faz muito sentido, e conferimos a esse pico um nome especial: dizemos que ele é o principal valor do deslocamento de fase. Para encontrarmos esse pico, sempre podemos adicionar/subtrair 2␲ do deslocamento de fase, até que ele esteja no intervalo que desejamos.

4.5

Amplitudes

A amplitude descreve uma quantidade sem atribuir unidades a ela. A magnitude também é uma descrição sem unidade, com uma diferença: a amplitude pode ser positiva ou negativa, enquanto a magnitude é sempre positiva [12]. |x(t)| refere-se à magnitude e, como é de se esperar, a função abs em MATLAB retorna a magnitude. Muitas pessoas em DSP utilizam a magnitude e a amplitude de forma intercambiável, por uma boa razão. Ao visualizarmos um componente da senoide como um fasor, nós a desenhamos em sua posição inicial sem nos preocuparmos com sua rotação com o tempo. Imagine desenhar o fasor ao longo do eixo x positivo e depois girá-lo no sentido anti-horário de acordo com o ângulo de fase. Uma extensão negativa significa que desenharíamos o fasor ao longo do eixo x negativo e depois giraríamos (novamente no sentido anti-horário) de acordo com o ângulo. Entretanto, isso é exatamente o mesmo que desenhar a extensão ao longo do eixo x positivo e girá-la em ␲ radianos adicionais (180°). Um fasor com uma extensão negativa seria o mesmo que um fasor com uma extensão positiva, mas com um giro de meia revolução, conforme demonstrado na Figura 4.6.

Eixo imaginário

Eixo imaginário

j

j a Eixo real



␪ Eixo real

–a

Figura 4.6

Weeks 004.indd 115

Um fasor de −a no ângulo ␪ é igual a a no ângulo (␪ + ␲).

04/04/12 19:07

116

CAPÍTULO 4

Agora que sabemos que um fasor com extensão negativa pode ser representado de modo alternativo por uma extensão positiva adicionando ␲ ao ângulo, como isso se aplica a senoides que variam com o tempo? Considere duas senoides de mesma frequência, x1 = a × cos(2␲ft + ␾1) e x2 = −a × cos(2␲ft + ␾2). Sabemos que os valores mínimo e máximo para a função cosseno são −1 e +1, respectivamente. Portanto, x1 varia entre +a e −a, e x2 varia entre −a e +a. Visto que as duas senoides possuem a mesma frequência, elas devem ser muito semelhantes. Na verdade, a única diferença entre elas são os seus ângulos de fase, uma diferença de ␲. Ou seja, ␾2 = ␾1 + ␲. Isso faz muito sentido considerando-se a conversão de um valor complexo de x + jy para a forma polar r ⬔␪: a equação ( ) conduz a um valor positivo para r. Essa é a razão pela qual utilizamos um valor positivo para a amplitude. A principal exceção fica por conta do componente DC (direct current ou corrente contínua), onde a frequência é igual a zero. O ângulo de fase também é considerado igual a zero e, portanto, a amplitude é o único valor que resta para se determinar o sinal do componente DC.

4.6

Revisão de Números Complexos

Em vez de utilizar o sistema de coordenadas cartesianas para mapear uma variável e uma função, podemos representar um número complexo como um ponto nesse plano. Aqui temos o eixo x representando a parte real de um número complexo e o eixo y representando a parte imaginária desse número. Naturalmente, também podemos pensar nessa mesma informação em termos de coordenadas polares; ou seja, podemos representá-la como um ponto (x, y) ou como um fasor r ⬔␪ (extensão e ângulo). (Veja a Figura 4.7.) A posição x do fasor é fornecida pela função x = r cos(␪), onde r é o raio (ou a extensão do fasor) e ␪ é o ângulo entre o fasor e o eixo x. A coordenada y do fasor segue como y = r sin(␪). Essas funções originam-se da geometria, como mostra a Figura 4.8. Um fasor bidimensional, com um segmento de linha vertical de y unidades de altura, forma um triângulo retângulo com o eixo x.

Eixo imaginário (x, y)

j r

␪ Eixo real

Figura 4.7

Um número complexo pode ser exibido como um ponto ou um fasor 2D.

r

y

y = r sin (␪ ) y = r cos (␪ )

x

Figura 4.8

Weeks 004.indd 116

Um fasor forma um triângulo retângulo com o eixo x.

04/04/12 19:07

Senoides

117

Com o uso da notação j, podemos representar a posição do fasor no plano complexo como r cos(␪) + j r sin(␪). Lembre-se da fórmula de Euler, e j ␪ = cos(␪) + j sin(␪) ou, multiplicando por uma constante, r cos(␪) + j r sin(␪) = r e j ␪ Assim podemos substituir r e j ␪ para representar esse fasor (chamado alternativamente de vetor de fase). Imagine que um vetor bidimensional comece no eixo x e gire no sentido anti-horário em torno da origem (veja a Figura 4.9). À medida que ele gira, a extensão do vetor permanece a mesma, mas o ângulo que ele faz com o eixo x positivo sempre aumenta. Eventualmente, o vetor terá percorrido integralmente o perímetro e momentaneamente se alinhará com o eixo x positivo novamente, antes de repetir sua jornada em torno da origem. Trata-se de um movimento muito semelhante ao do ponteiro de segundos no mostrador de um relógio, exceto que na direção oposta. É isso que queremos dizer com o termo vetor girante*, também chamado de fasor girante. No sistema de coordenadas cartesianas, podemos modelar um vetor girante em termos de funções seno e cosseno. Tal como na Figura 4.8, a posição x do vetor é fornecida pela função x = r cos(␪), com o valor de ␪ elevando-se continuamente com o tempo. Como antes, encontramos a coordenada y do vetor com a equação y = r sin(␪). A diferença-chave aqui é que ␪ muda com o tempo e, como consequência, x e y mudam com o tempo também.

Figura 4.9

Um fasor girante.

Uma senoide simples possui a forma a × cos(2␲ ft + ␾). Com o uso da fórmula de Euler, ela pode ser representada como Real{a × e j(2␲ f t+␾)}. Essa forma é interessante por facilitar algumas operações. Por exemplo, ela pode ser reescrita como Real{a × e j ␾e j2 ␲ ft}. Isso significa que você pode encará-la como a parte real do resultado do fasor a × e j ␾ multiplicado pelo fasor girante e j2␲ ft de extensão unitária. A fórmula inversa de Euler oferece uma forma ainda melhor de representação.

Aqui temos um cosseno real decomposto em dois fasores girantes complexos. Conforme veremos em breve, as partes complexas se cancelam mutuamente. Novamente, a notação do fasor nos ajuda a manipular matematicamente esses sinais. Eis alguns outros conceitos oriundos da matemática que vale a pena relembrar:

Se ea = eb, então a = b. A seguir, vamos revisar algumas propriedades de j.

* Ou vetor rotativo. (N.T.)

Weeks 004.indd 117

04/04/12 19:07

118

4.7

CAPÍTULO 4

Algumas Propriedades Interessantes de j

A notação angular representa números complexos (ou números polares), como:

Pense no plano complexo. A raiz quadrada de −1 define j, o que significa que desenhamos isso como um vetor unitário apontando ao longo do eixo imaginário na direção positiva. Agora considere um vetor de extensão unitária girado em 90° no sentido anti-horário. Esse vetor também repousaria sobre o eixo imaginário, apontando para a mesma direção. Ambas as representações significam a mesma coisa. Note que obteríamos esses mesmos números se encontrássemos a magnitude e o ângulo do fasor cartesiano 0 + 1j. A notação angular nos permite especificar um fasor no plano complexo. A multiplicação por j ou −j pode ser interpretada como uma rotação de tal fasor no plano complexo. Esta seção demonstra essa propriedade.

4.7.1

Rotação no Sentido Anti-Horário

A multiplicação por j pode ser imaginada como uma rotação de 90° no sentido anti-horário [20]. Se começarmos com um número real – digamos, 1 – e o multiplicarmos por j, terminaremos com j.

Em seguida, poderíamos multiplicar isso por j novamente: j 2 = −1. E novamente:

E novamente: j 4 = 1. Portanto, terminamos com o mesmo número com o qual começamos. Se traçássemos isso no plano complexo, constataríamos que cada multiplicação por j é equivalente à rotação do ponto em 90° no sentido antihorário.

4.7.2

Rotação no Sentido Horário

Também podemos encontrar uma versão equivalente de j −1 com o seguinte: j 2 = −1. Se dividirmos ambos os lados por j,

Se trocarmos os lados e multiplicarmos ambos por −1,

Weeks 004.indd 118

04/04/12 19:07

Senoides

119

Na verdade, a multiplicação por j −1 ou – j equivale a uma rotação na direção horária. Podemos provar que isso é válido para o caso geral com o número complexo a + jb.

Se traçássemos a sequência anterior, considerando que a e b são ambos positivos, veríamos o vetor criado a partir da origem até o ponto no plano complexo girar na direção horária. A Figura 4.10 mostra isso, com b menor que a. (A título de exercício, verifique se obtemos o mesmo resultado quando b > a.) Podemos ver claramente que o ponto a + jb gira em torno da origem, em incrementos de −␲/2. Os números próximos aos pontos mostram a sequência; se multiplicássemos o ponto por j, veríamos a ordem invertida.

j 4

a 1

b –a 3

–b

b

a

Real

–b –a 2

Figura 4.10

4.7.3

Um vetor gira −␲/2 no sentido horário quando multiplicado por −j.

Remoção de j em

Assim como a fórmula quadrática utiliza os sinais ± para indicar duas possibilidades para raízes, devemos ser cautelosos ao removermos j do radical. Considere a como algum valor real positivo. Ao lidarmos com , podemos ser tentados a reescrevê-lo como . E podemos estar corretos, mas existe uma segunda também pode funcionar. Ou seja, Portanto, possibilidade a ser considerada:

4.8

De Onde Vem e?

De onde vem e? Em primeiro lugar, e é um número irregular: e = 2.71828… Ele origina-se da ideia de juros [21]. Suponha que você empreste a duas pessoas a mesma quantidade de dinheiro por uma taxa de juros fixa. Se a primeira pessoa lhe paga o empréstimo e os juros dentro de duas semanas, enquanto a segunda pessoa espera um mês antes de reembolsá-lo, você pode pensar que a segunda pessoa deve pagar um juro adicional. Afinal de contas, se você tivesse o dinheiro de volta mais cedo, talvez pudesse tê-lo

Weeks 004.indd 119

04/04/12 19:07

120

CAPÍTULO 4

Tabela 4.1

Juros compostos sobre US$1000 aproximam-se de US$1000 × e. n Montante a reembolsar 10 2593.74 100 2704.81 1000 2716.92 10,000 2718.15 100,000 2718.27 250,000 2718.28 500,000 2718.28

emprestado a outra pessoa, à mesma taxa de juros. Assim, na próxima vez em que emprestasse dinheiro a alguém, poderia informar a essa pessoa que espera o reembolso dentro de um mês, mas que computará os juros em base diária. Toda vez que você calcula o montante a ser reembolsado, o juro aplica-se não apenas ao montante do empréstimo original, mas também aos juros que o dinheiro auferiu até aquele ponto. Essa é a origem, portanto, dos juros compostos. Os juros simples, por outro lado, funcionam da seguinte maneira: suponha que emprestemos US$ 1000, com uma data de vencimento especificada. Quando o prazo vencer, o mutuário nos reembolsará US$ 1000 mais os juros. Se o mutuário decidir não nos reembolsar, esperaremos outro período de tempo mediante o acordo de que o mutuário nos reembolsará US$ 1000 mais 2 × os juros. Se esse padrão prosseguir, eventualmente o mutuário nos deverá o dobro do montante original, ou seja, US$ 2000. Mas a taxa de juros permanece a mesma. Ela custa ao mutuário tanto para adiar o pagamento dos US$ 2000 quanto custa para os US$ 1000 originais. Ou seja, a uma taxa de juros de 5%, o mutuário deveria um total de US$ 1000 + US$ 50 de juros após o primeiro período de tempo. Mas, após 20 períodos de tempo, o mutuário deveria US$ 1000 + US$ 950 de juros até aquele ponto, mais US$ 50 pelo 20.o período de tempo. Não deveríamos cobrá-lo mais por reter tanto nosso dinheiro? Um acordo mais vantajoso (para nós, mutuantes) seria a aplicação de juros compostos, na qual cobramos os juros não apenas sobre o montante original emprestado, mas sobre o saldo do dinheiro emprestado até aquele ponto. Com o exemplo dos US$ 1000 com juros de 5%, o mutuário nos deveria US$ 1000 + US$ 1000 × 0.05 = US$ 1050 para o primeiro período de tempo e US$ 1050 + US$ 1050 × 0.05 = US$ 1102.50 para o segundo período de tempo. O montante devido ao final de um período de tempo é igual a (montante devido ao final do último período de tempo) × (1 + taxa de juros). Se levarmos isso a um extremo, onde o tempo entre os cálculos de juros se torne arbitrariamente pequeno, chegaremos à equação a seguir, na qual x é a taxa de juros (x = 1 significa que dobramos nosso dinheiro) e n é o número de iterações. Por exemplo, com x = 1 esperamos obter juros simples de 100% no tempo designado. Para juros complexos, dividimos o tempo de reembolso em n segmentos, o que significa que reavaliamos o montante que o mutuário nos deve n vezes dentro do tempo designado. Com x = 1 e n = 10, o mutuário reembolsa US$ 200 para juros simples e US$ 259.37 para juros compostos.

ou

O programa a seguir demonstra como e tem origem nos juros compostos. Suponha que emprestemos US$ 1000 a uma taxa de juros de 100%, dobrando nosso dinheiro com juros simples. Com os juros compostos, escolhemos a frequência de cálculo dos juros, e quanto mais alta ela for, mas próximos de e × juros nós chegaremos. A Tabela 4.1 foi gerada utilizando-se o programa a seguir. Como podemos ver a partir da tabela, à medida que n aumenta, o montante a reembolsar aproxima-se de US$ 1000 × e (o valor de e ⬇ 2.71828). Note que o aumento de n não significa que emprestamos dinheiro por um período mais longo, mas que simplesmente estipulou-se um período menor entre o recálculo dos juros. A questão é que, à medida que (1/n) se aproximar de 0, o valor do montante composto será e × principal, em vez de 2 × principal. A Figura 4.11 exibe um gráfico de juros simples (linha sólida) versus juros compostos (linha de traço e ponto). Em outras palavras, e é o limite do montante composto continuamente. Agora podemos responder à pergunta sobre como a fórmula de Euler funciona.

Weeks 004.indd 120

04/04/12 19:07

Senoides

121

Juros simples (sólida) versus compostos (traço e ponto) 2800 2600 2400

Dinheiro

2200 2000 1800 1600 1400 1200 1000

1000

2000

3000

4000 5000 6000 7000 Iterações de composição

8000

9000 10000

Figura 4.11 Juros simples versus compostos.

% interest.m % Juros simples versus juros compostos % -> e % x = 1; % Set x = 1 to find e n = 100; principal = 1000; disp('Este programa mostra como e origina-se dos juros compostos.'); disp(sprintf('Emprestimo de %6.2f a %d por cento de juros. ', ... principal, round(x*100))); simple = principal; compound = principal; % Calcule os juros simples simple = simple + principal*x; % Calcule os juros compostos for i=1:n compound = compound*(1+ x/n); % + meus_juros; end disp('Montante a reembolsar:'); disp(sprintf(' com juros simples = %6.2f', simple)); disp(sprintf(' com juros compostos = %6.2f', compound)); disp(sprintf(' composto %d vezes.', n)); O site da LTC Editora também inclui uma versão diferente desse programa, chamada interest2.m, que produz o gráfico exibido na Figura 4.11.

Weeks 004.indd 121

04/04/12 19:07

122

CAPÍTULO 4

4.9

A Fórmula de Euler

Agora estamos prontos para conhecer a origem da fórmula de Euler. Para descobrirmos como a fórmula de Euler funciona, precisamos primeiramente do teorema binomial [21]. e x = 1 + x + x 2/2! + x 3/3! + x 4/4! + x 5/5! + . . . Substituiremos x por j␪, em que ␪ é apenas uma variável. e j ␪ = 1 + j ␪ + (j ␪)2/2! + (j ␪)3/3! + (j ␪)4/4! + (j ␪)5/5! + . . . Agora simplifique: e j ␪ = 1 + j ␪ − ␪ 2/2! − j ␪ 3/3! + ␪ 4/4! + j ␪ 5/5! + . . . Em seguida, decomponha os resultados nas partes real e imaginária: e j ␪ = (1 − ␪ 2/2! + ␪ 4/4! + . . . ) + j(␪ − ␪ 3/3! + ␪ 5/5! + . . . ).

(4.4)

Vamos deixar isso de lado por enquanto. Lembramos, então, a fórmula de Maclaurin [22]: (4.5) Ao substituirmos f(x) = sin(x), obtemos:

em que y > 0 e y < x. As derivadas da função seno são as seguintes [22]:

Agora substitua f (x) e suas derivadas pelas senoides.

Após a simplificação e a substituição de ␪ por x: (4.6) A função cosseno pode ser decomposta de forma semelhante.

Weeks 004.indd 122

04/04/12 19:07

Senoides

123

Ao substituirmos f(x) da Equação 4.5 por cos(x), temos:

Após a simplificação e a substituição de ␪ por x: (4.7) Agora repita a Equação 4.4, mas observe como as partes reais correspondem à função cos(␪) (veja a Equação 4.7) e como as partes imaginárias correspondem à função sin(␪) (veja a Equação 4.6).

Isso significa que essa equação pode ser simplificada para: e j ␪ = cos(␪) + j sin(␪) que, naturalmente, é a fórmula de Euler.

4.10

Forma Alternativa da Equação de Euler

Conforme vimos anteriormente, a equação de Euler é e j ␪ = cos(␪) + j sin(␪). O que aconteceria se por acaso ␪ fosse um número negativo? Considere ␪ = −␾ e depois substitua ␪ por −␾ na equação anterior. Tenha em mente que realmente não importa o símbolo que utilizamos, desde que sejamos consistentes. No lado direito, podemos substituir as funções seno e cosseno por um par de identidades trigonométricas, ou seja, cos(−␾) = cos(␾) e sin(−␾) = −sin(␾). Assim, a equação de Euler também funciona para um expoente negativo, ou seja, e j(−␾) = cos(−␾) + j sin(−␾) ou, simplesmente, e −j ␾ = cos(␾) + j sin(␾).

4.11

A Fórmula Inversa de Euler

A fórmula inversa de Euler é dada por:

Isto é, uma senoide pode ser substituída por um fasor de meia amplitude, mais um fasor similar com um ângulo negativado. Chamamos esse fasor de seu conjugado complexo e denotamos o conjugado complexo de z com o símbolo z*. Esses fasores são sempre imagens espelhadas um do outro em relação ao eixo x. A Figura 4.12 mostra isso para três ângulos de exemplo diferentes, ␪ (no primeiro quadrante), ␾ (no segundo quadrante) e ␻ (no terceiro quadrante). O fasor e seu conjugado complexo estão em lados opostos do eixo x real, mas do mesmo lado do eixo y imaginário, independentemente do valor do ângulo. Se o fasor girar, o conjugado complexo também girará, mas em direção oposta.

Weeks 004.indd 123

04/04/12 19:07

124

CAPÍTULO 4

e j␪

e –j␪

e –j␻ e j␾

e –j␾ e j␻

Figura 4.12 Fasores e seus conjugados complexos.

Pense na função cosseno como a parte real de um vetor complexo, possivelmente até mesmo de um que gira. A função cosseno também pode ser imaginada como o resultado da soma de dois vetores que estão girando em direções opostas. Isso acontece porque a soma dos dois cancela as partes imaginárias. Podemos verificar essa fórmula com a fórmula de Euler, da seguinte forma:

A partir da análise anterior, obtemos a fórmula inversa de Euler:

Exemplo 4.1 Fornecido x(t) = 3e j ␲/6e j2␲1000t, represente graficamente x(0). Qual a importância disso? Resposta: Quando t = 0, o termo e j2␲ft torna-se 1, restando-nos portanto 3e j ␲/6, um fasor (não girante). A extensão do vetor é dada pela amplitude – neste caso, 3 unidades. O termo ␲/6 indica que ele se encontra no primeiro quadrante. A importância de se mostrar o vetor no instante 0 é que ele revela o offset determinado pelo deslocamento de fase [7]. Observe que a frequência de 1000 Hz não influi em tal questão. Para um exemplo desse gráfico, veja a Figura 4.13. Note como é fácil obter a extensão e o ângulo de fase a partir dessa representação.

3

␪ /6

Figura 4.13 Gráfico de x(0) em que x(t) = 3e j ␲/6e j2␲1000t.

Weeks 004.indd 124

04/04/12 19:07

Senoides

125

A frequência f controla a velocidade de giro do fasor. Na verdade, se a frequência for negativa, o vetor girará na direção oposta (horária). Você pode verificar isso examinando os valores x e y ao longo de pequenos incrementos de tempo para uma frequência positiva (por exemplo, 10 Hz) e para uma frequência negativa (por exemplo, −10 Hz). O código a seguir mostra isso. f=10; t=0:0.01:1; x1 = cos(2*pi*f*t+pi/4); x2 = cos(2*pi*(-f)*t+pi/4); plot(1:length(x1),x1,'r',1:length(x2),x2,'b') O termo e j ␪ geralmente nos fornece uma quantidade complexa, mas, sob determinadas condições, ela se reduz a um número real somente. Um exemplo disso ocorre quando ␪ = ␲. Aqui nós calculamos e± j ␲, começando pelas duas formas da fórmula de Euler.

Como podemos ver, quando o ângulo ␪ = ± ␲, a expressão e j ␪ reduz-se a um valor sobre o eixo real. Isso também funciona para outros valores de ângulo, a saber, ␪ = 0.

4.12

Manipulação de Números Complexos

Números complexos podem ser escritos como uma representação real e imaginária, ou em coordenadas polares. Nesta seção analisaremos algumas formas de manipulação de números complexos com vistas à simplificação. A discussão aqui mostra os números complexos traçados no plano complexo. Assim como os números complexos, os vetores podem ser somados. De fato, em geral as aulas introdutórias de física demonstram isso graficamente, movendo um vetor bidimensional até que sua extremidade posterior conecte-se à extremidade anterior do outro. O vetor resultante pode então ser desenhado a partir da origem da extremidade anterior do vetor que foi movido. No que se refere à adição, isso também se aplica aos números complexos.

4.12.1

Soma de Dois Números Complexos

Normalmente a soma de números complexos não pode ser executada na forma de coordenadas polares, mas é de fácil execução na forma cartesiana. Por exemplo, como se somaria 3⬔␲/6 + 4⬔5␲/18? Isto é, considere z 1 = 3⬔␲/6, z 2 = 4⬔5␲/18 e z 3 = z 1 + z 2. Convertemos z 1 na forma cartesiana:

Convertemos z 2 na forma cartesiana:

Weeks 004.indd 125

04/04/12 19:07

126

CAPÍTULO 4

Somamos os dois:

Convertemos da forma cartesiana para a forma polar:

Portanto, z 3 = 6.89⬔0.723 radianos. Naturalmente, o arredondamento acarretará alguma imprecisão.

4.12.2

Soma de Números Complexos em Geral

Assim como podemos somar dois números complexos na forma r⬔␾, podemos somar dois números complexos na forma re j ␪.

Ao convertermos em senoides com a fórmula de Euler:

Para manter o caráter geral, consideraremos a = (r1 cos(␪1) + r2 cos(␪2)) e b = (r1 sin(␪1) + r2 sin(␪2)), de modo que o resultado anterior torna-se a + jb. Agora podemos converter esse número complexo na forma polar complexa.

e

␪ 3 = arctan(b/a). Tenha em mente os ajustes de ±␲ caso a seja negativo. Desde que a e b tenham sido computados conforme mostrado, poderemos encontrar r3 e ␪3 resultantes.

4.12.3

Soma de Fasores Girantes

Isso também funciona no caso de fasores girantes, considerando-se que eles possuem a mesma frequência.

em que a3 e ␾3 podem ser encontrados conforme mostrado (para os fasores não girantes).

Weeks 004.indd 126

04/04/12 19:07

Senoides

4.12.4

127

Soma de Senoides de Mesma Frequência

De um modo semelhante ao utilizado para a soma de fasores girantes, você também pode somar duas senoides quando as frequências são as mesmas. Por exemplo, a 1 cos(2␲ ft + ␾1) + a 2 cos(2␲ ft + ␾ 2) = a 3 cos(2␲ ft + ␾ 3). A Seção 4.13 mostra como obter os valores para a3 e ␾ 3.

4.12.5

Multiplicação de Números Complexos

A Figura 4.14 mostra dois números complexos, z 1 e z 2, traçados como coordenadas polares. Se nós os somarmos (veja a Figura 4.15): z3 = z1 + z2 = 5 + 3j + 6 + 2j = 11 + 5j. Se nós os multiplicarmos: z 4 = z 1 z 2 = (5 + 3j)(6 + 2j) = 30 + 10j + 18j + 6j 2 = 30 + 28j − 6 = 24 + 28j.

(5, 3) (6, 2)

z1 = 5 + 3j z2 = 6 + 2j Figura 4.14 Dois exemplos de números complexos.

z3 (5, 3) (6, 2)

z1 = 5 + 3j z2 = 6 + 2j z3 = z1 + z2 = 11 + 5j

Weeks 004.indd 127

Figura 4.15 Soma de dois exemplos de números complexos.

04/04/12 19:07

128

CAPÍTULO 4

30

20

10

z3 (5, 3) (6, 2)

z1 = 5 + 3j z2 = 6 + 2j z3 = z1 + z2 = 11 + 5j z4 = z1 z2 = 24 + 28j

10 Figura 4.16

20

30

Soma e multiplicação de dois exemplos de números complexos.

Eles também podem ser multiplicados em forma polar:

A Figura 4.16 mostra o resultado da multiplicação para os dois números complexos do exemplo, incluindo o resultado da soma.

4.13

Soma de Fasores Girantes: Um Exemplo

Você pode somar dois (ou mais) fasores girantes (ou senoides) se eles tiverem a mesma frequência. Mas, se você somá-los, como serão os resultados? Eis um exemplo: x 1(t) = 2 cos(2␲ 100t + ␲/4) x 2(t) = 3 cos(2␲ 100t + ␲/8) x 3(t) = x 1(t) + x 2(t).

Weeks 004.indd 128

04/04/12 19:07

Senoides

129

Se utilizarmos a fórmula inversa de Euler,

Obteremos o seguinte para x 1(t) e x 2(t).

Agora simplificamos essas expressões.

Em seguida, somamos as duas para encontrar x 3(t).

Para avançarmos, precisamos converter esses números em coordenadas cartesianas. Em vez de uma conversão explícita em coordenadas cartesianas (x, y), é mais fácil utilizar a fórmula de Euler, sabendo-se que x corresponde à parte real e y corresponde à parte imaginária. Na verdade, utilizamos j apenas para manter os valores de x e y organizados. Primeiramente, consideramos alguns componentes da expressão anterior para x 3(t).

Em seguida, nós os somamos.

Uma resposta numérica gera algum erro de arredondamento. ⬇2.093 + j1.296 Com essa solução aproximada, nós a convertemos em um raio e um ângulo.

Agora podemos utilizá-los para encontrar uma versão mais simples de x3(t):

Com a fórmula de Euler,

Weeks 004.indd 129

04/04/12 19:07

130

CAPÍTULO 4

Os termos j sin( ) cancelam um ao outro, pois sin(−␾) = − sin(␾). Como cos(−␾) = cos(␾), os termos em cosseno simplesmente se somam. x3(t) ⬇ 4.92 cos(2␲ 100t + 8␲/45) Utilizamos o MATLAB para validar esses resultados. Na Figura 4.17, duas senoides são traçadas na parte superior do gráfico, correspondendo ao exemplo anterior, x 1(t) = 2cos(2␲100t + ␲/4), traçada como uma linha sólida contendo pontos, e x 2(t) = 3cos(2␲100t + ␲/8), traçada como a linha sólida. A parte inferior do gráfico também mostra duas senoides, embora coincidentemente elas estejam sobrepostas. A primeira, traçada como uma linha sólida, foi encontrada com o comando x3 = x1 + x2;. Ou seja, tratase de uma soma ponto a ponto dos sinais x1 e x2. A segunda senoide corresponde a 4.92cos(2␲100t + 8␲/45), que vem a ser o resultado analítico obtido anteriormente. Vemos que os resultados coincidem. O programa a seguir foi utilizado para gerar esse gráfico. % % adding_sins.m % Mostre 2 senoides de mesma frequencia e como se parecem combinadas. % t = 0:0.001:0.2; x1 = 2*cos(2*pi*100*t + pi/4); x2 = 3*cos(2*pi*100*t + pi/8); x3 = x1 + x2; x4 = 4.92*cos(2*pi*100*t + 8*pi/45); subplot(2,1,1); plot(t, x1, 'b.-', t, x2, 'k');

2cos(2␲ 100t + ␲/4) (.) e 3cos(2␲ 100t + ␲/8) (sólida) 2 1 0 –1 –2 20

40

60

80

100 120 Tempo (seg.)

140

160

180

200

2cos(2␲ 100t + ␲/4) + 3cos(2␲ 100t +␲ /8) somadas (sólida), analítica (.) 4 2 0 –2 –4 20

Figura 4.17

Weeks 004.indd 130

40

60

80

100 120 Tempo (seg.)

140

160

180

200

Duas senoides de mesma frequência somadas ponto a ponto e analiticamente.

04/04/12 19:07

Senoides

131

mystr = '2*cos(2*pi*100*t + pi/4) (.) e'; mystr = strcat(mystr,' 3*cos(2*pi*100*t + pi/8) (solida)'); title(mystr); xlabel('tempo (seg.)'); axis tight; subplot(2,1,2); plot(t, x3, 'b', t, x4, 'r.-'); mystr = '2cos(2pi100t + pi/4) + 3cos(2pi100t + pi/8) somadas'; title(strcat(mystr,' (solida), analítica (.)')); xlabel('tempo (seg.)'); axis tight; A adição de dois fasores pode ser vista graficamente como na Figura 4.18. Aqui tomamos o fasor e j 2␲ ft a 1e j ␾1 e o somamos ao fasor e j2␲ ft a 2e j ␾2. Uma vez que os fasores girantes são os mesmos, nós os separamos e somamos o resto, assim como podemos reescrever ax + bx como x(a + b). Isso funciona até mesmo no caso de os fasores não girantes possuírem extensões diferentes. Para a soma de fasores não girantes (como vamos do centro da figura até a base), consulte novamente a Figura 4.15. e j2␲ ft a 3e j ␾3 = e j2␲ ft(a 1e j ␾1 + a 2e j ␾2) Aqui, dois fasores girantes são mostrados como sua parte girante, multiplicados por um fasor com a amplitude e o ângulo de fase. Se eles estiverem girando na mesma frequência, os componentes do fasor girante serão exatamente os mesmos e os componentes da amplitude e ângulo de fase poderão ser somados entre si.

+

Fasor girante

=

Fasor

=

Fasor

Fasor

)

Fasor

(

Fasor girante Figura 4.18

Weeks 004.indd 131

+

(

Fasor girante

Fasor girante

Fasor Uma representação gráfica da soma de dois fasores de mesma frequência.

04/04/12 19:07

132

CAPÍTULO 4

r1

␪1

+ r1

Fasor girante

r2

+ Fasor girante

Fasor girante

Fasor

␪1

Fasor

+

r2

Fasor girante

Fasor

– ␪1

– ␪2

Fasor

r3

␪3

=

+ r3

Fasor girante Figura 4.19

Fasor

Fasor girante

– ␪3

Fasor

Uma representação gráfica da soma de duas senoides de mesma frequência.

Agora vamos dar um passo além. Em vez de simplesmente somar dois fasores, somaremos duas senoides (veja a Figura 4.19). Lembre-se de que a fórmula inversa de Euler nos fornece dois fasores de meia amplitude para um termo em cosseno, ou seja:

Nessa figura, tomamos dois componentes senoidais típicos (de mesma frequência) a 1 × cos(2␲ ft + ␪1) e a 2 × cos(2␲ ft + ␪2), visualizamos os dois como fasores girantes multiplicados por fasores não girantes e visualizamos como eles são somados. Observe que rn = an/2 nessa figura. a 3 cos(2␲ ft + ␪3) = a 1 cos(2␲ ft + ␪1) + a 2 cos(2␲ ft + ␪2) A primeira linha representa a1 cos(2␲ f t + ␪1), mostrada como um par de fasores girando em direções opostas. Sua origem é a fórmula inversa de Euler, onde uma função cosseno é fornecida como uma combinação de dois fasores (de metade da amplitude). Suas rotações em direções opostas significam que as partes complexas cancelam uma a outra e que somente a parte correspondente ao cosseno permanece. A segunda linha representa a 2 × cos(2␲ ft + ␪2), novamente como um par de fasores girando em direções opostas, conforme fornecido pela fórmula inversa de Euler. Na terceira linha temos uma visualização do resultado da soma (mantendo os lados esquerdo e direito separados). Note como os lados esquerdo e direito são semelhantes – eles são conjugados complexos um do outro.

Weeks 004.indd 132

04/04/12 19:07

Senoides

4.14

133

Multiplicação de Fasores

Nós multiplicamos dois fasores colocando-os em forma exponencial, multiplicando suas amplitudes e somando seus argumentos angulares. Para vermos como a soma de ângulos funciona, vamos analisar como um fasor girante multiplicado por outro fasor e uma amplitude se comporta quando os multiplicamos utilizando a fórmula de Euler. Eis aqui um par de equações envolvendo a soma de argumentos de senoides [22], de que necessitaremos em breve. cos(␪1 + ␪2) = cos(␪1) cos(␪2) − sin(␪1) sin(␪2) sin(␪1 + ␪2) = cos(␪1) sin(␪2) + sin(␪1) cos(␪2). Em vez de escrevermos 2␲ f seguidamente, nós o substituiremos por ␻. Com estes itens em mente, podemos simplificar o seguinte: a × e j ␪ e j ␻t = a × (cos(␪) + j sin(␪))(cos(␻t) + j sin(␻t)) = a × cos(␪) cos(␻t) + ja × sin(␪) cos(␻t) + ja × cos(␪) sin(␻t) + j 2a × sin(␪) sin(␻t) = a × cos(␪) cos(␻t) − a × sin(␪) sin(␻t) + ja × sin(␪) cos(␻t) + ja × cos(␪) sin(␻t) = a × (cos(␪) cos(␻t) − sin(␪) sin(␻t)) + ja × (cos(␪) sin(␻t) + sin(␪) cos(␻t)) = a × cos(␪ + ␻t) + ja × sin(␪ + ␻t). Devemos ver agora como a multiplicação de dois exponenciais simplifica-se para a adição de seus argumentos. Naturalmente, um atalho seria substituir eced por ec+d, ou, mais especificamente, substituir e j ␪ej ␻ t por e j(␪+␻ t). De qualquer modo, chegamos ao mesmo resultado. Se multiplicarmos dois expoentes complexos, z3 = z1z2,

Portanto,

Simplesmente multiplicamos as amplitudes e somamos os argumentos angulares. Também podemos encontrar z 3 = z 1z 2 quando z 1 e z 2 são números complexos. Ou seja, suponha que z 1 = x 1 + jy 1 e z 2 = x 2 + jy 2. Assim, z3 pode ser encontrado multiplicando-se os termos. z 3 = x 1 x 2 + jx 1 y 2 + jx 2 y 1 − y 1y 2 A multiplicação de fasores pode ser efetuada facilmente agora que conhecemos o padrão.

4.15

Sinais harmônicos

Os sinais muitas vezes são representados como uma soma de senoides. Quando essas senoides são correlatas, chamamos o sinal de um harmônico. Os sinais do mundo real podem ser decompostos em um conjunto de senoides somadas. Um sinal harmônico é composto por diversas senoides somadas, cada qual com um múltiplo inteiro de alguma frequência base, chamada de frequência fundamental. Se, por exemplo, pressionarmos uma tecla em um piano,

Weeks 004.indd 133

04/04/12 19:07

134

CAPÍTULO 4

será gerada não apenas uma frequência, mas diversas outras baseadas em múltiplos inteiros da frequência fundamental. O sinal a seguir é um harmônico: x(t) = 0.4 cos(2␲ 15t + ␲/5) + 0.7 cos(2␲30t) + cos(2␲45t − ␲/4). O sinal x(t) é da forma a1 × cos(2␲(1)f0t + ␾1) + a2 × cos(2␲(2)f0t + ␾2) + a3 × cos(2␲(3)f0t + ␾3) onde, para x(t), a1 = 0.4, a2 = 0.7, a3 = 1, ␾1 = ␲/5, ␾2 = 0, ␾3 = −␲/4 e f0 = 15 Hz. Qualquer sinal dessa forma é um harmônico. Se um valor de amplitude for 0, a senoide correspondente não contribuirá para a função. Em outras palavras, 0 × cos(alguma coisa) vale 0. Se temos uma função como x2(t) = 0.1 cos(2␲100t − ␲/6) + 1.3 cos(2␲300t + ␲) + 0.5 cos(2␲400t + 2␲/3) então x2(t) é um harmônico com a1 = 0.1, a2 = 0, a3 = 1.3, a4 = 0.5, ␾1 = −␲/6, ␾2 = 0, ␾3 = ␲, ␾4 = 2␲/3 e f0 = 100 Hz. A função MATLAB a seguir, plotharmonic.m (no site da LTC Editora) traça sinais harmônicos quando são fornecidas a frequência fundamental e listas para as magnitudes e fases. function plotharmonic(freq, mag, phase) num_points = 200;

% Numero de pontos por repeticao

% Queremos isso para 2 repeticoes e num_points por repeticao. step = 2/(freq*num_points); t = 0:step:2*(1/freq); x = 0; %% Simule a soma de uma onda cossenoide para cada magnitude e fase for i=1:length(mag) x = x + mag(i)*cos(2*pi*i*freq*t + phase(i)); end plot(t, x); A função faz algumas outras coisas, omitidas aqui. Ela faz uma verificação para certificar-se de que os parâmetros mag e phase possuem a mesma extensão. Além disso, ela rotula o traçado e cria um título apropriado. Por exemplo, os comandos a seguir traçam a x2(t) anterior. Mag = [ 0.1 0 1.3 0.5]; Phase = [ -pi/6 0 pi 2*pi/3]; plotharmonic(100, Mag, Phase); % A frequencia fundamental e de 100 Hz O traçado resultante pode ser visto na Figura 4.20. Uma informação adicional é se existe um deslocamento vertical do sinal. Ou seja, o que aconteceria se o gráfico exibido na Figura 4.20 não estivesse centralizado em torno do eixo x? Isso pode ser obtido somando-se uma constante ao sinal. Se, por exemplo, adicionássemos 100 unidades ao sinal, ele pareceria o mesmo, mas ficaria centralizado em torno da linha y = 100. Essa informação pode ser acrescentada ao formato do sinal harmônico anterior simplesmente incluindo-se o termo a0cos(2␲(0)f0t + ␾0). Iniciar os múltiplos de f0 em zero significa que o primeiro componente é simplesmente uma constante. Não é preciso especificar um termo ␾0, pois cos(␾0) seria apenas uma constante e a0 já especifica isso. Portanto, consideraremos ␾0 igual a zero. Esse termo frequentemente é chamado de componente DC, de corrente direta (ou contínua), nome emprestado da engenharia elétrica.

Weeks 004.indd 134

04/04/12 19:07

Senoides

135

Sinal harmônico com quatro senoides

2

1.5

1

Amplitude

0.5

0

– 0.5

–1

– 1.5

–2 0

0.002

0.004

0.006

0.008

0.01 0.012 Tempo (seg.)

0.014

0.016

0.018

0.02

Figura 4.20 Um sinal harmônico.

Pense nos termos componente DC e fase como parâmetros para uma senoide imaginada. Podemos deslocá-la para a esquerda ou para a direita mudando seu ângulo de fase. Também podemos deslocá-la para cima ou para baixo, em relação ao eixo x, acrescentando um componente DC.

Exemplo 4.2 Para os sinais a seguir, aponte os que são harmônicos e os que não são. Se forem harmônicos, indique a frequência fundamental.

Resposta: Para ser harmonicamente correlata, cada senoide deve possuir uma frequência que seja um múltiplo inteiro de alguma frequência fundamental. Ou seja, elas devem estar na seguinte forma (lembre-se de que k é um inteiro):

Assim, colocamos os sinais anteriores nessa forma e verificamos se os valores de k são inteiros – o que eles devem ser para que as senoides sejam harmonicamente correlatas. Vamos examinar os argumentos da função cosseno para x1 e considerar k1 e k2 como valores de k.

Weeks 004.indd 135

04/04/12 19:07

136

CAPÍTULO 4

Se substituirmos f0, obteremos

Portanto, se k1 = 1, então k2 = 5

Deste modo, o sinal x1(t) possui valores de k inteiros e é harmônico. O sinal x3(t) também é. O sinal x2(t) não possui valores de k inteiros (devido à frequência de ␲) e, portanto, não é harmônico. O terceiro sinal, x3, é um pouco complicado pelo fato de os componentes de frequência não estarem alinhados da forma esperada. Com ele, f0 = 1 Hz, a0 = 1, ak = {3, 0, 0, 0, 0, 0, 2} e ␾k = {−␲/4, 0, 0, 0, 0, 0, ␲}. O sinal final do exemplo, x4(t), possui as mesmas frequências que x1(t), mais uma constante. Assim, ele também é harmônico.

4.16

Representação de um Sinal Digital Como uma Soma de Senoides

Um sinal digital pode ser representado como uma soma de senoides. As três figuras a seguir mostram como isso funciona. Podemos tomar um sinal digital e encontrar sua transformada de Fourier (a transformada de Fourier será analisada oportunamente neste livro). O aspecto importante a ser entendido é que

Original

8 7 6 5 4 3 2

0

1

2

3

4

5

6

7

5

6

7

Soma de senoides

8 7 6 5 4 3 2

0

Figura 4.21

Weeks 004.indd 136

1

2

3

4

Um sinal digital (acima) e sua representação de soma de senoides (abaixo).

04/04/12 19:07

Senoides

137

a transformada de Fourier nos fornece uma versão do sinal no domínio da frequência: uma lista de amplitudes e ângulos de fase correspondendo a um conjunto de senoides harmonicamente correlatas. Exploraremos a frequência fundamental dessas senoides mais tarde. Para a presente discussão, nós a ignoraremos. A temporização dessas amostras simuladas determina a frequência fundamental real, e portanto simplesmente nos ateremos aos números de amostra em vez do tempo para o eixo x dos traçados. Veja a Figura 4.21. Aqui notamos o sinal original, uma lista de pontos de amostra discretos. Sob ele, temos um gráfico de um sinal criado por meio da soma de diversas senoides (oito, para sermos precisos). Observe como esse sinal composto passa pelos mesmos pontos que os do sinal digital original. Se tivéssemos que ler valores a partir desse sinal composto com a mesma temporização que a da amostragem do sinal digital, obteríamos os mesmos dados. Na Figura 4.21, vemos que a representação de soma de senoides é contínua. Mas devemos tomar o cuidado de não concluir que o sinal analógico básico, que estamos tentando representar como um conjunto de pontos discretos, realmente se parece com o sinal composto. Podemos não saber como ele se parece, e a elevação que temos entre a amostra 2 e a amostra 3 (bem como entre a 3 e a 4) pode nos iludir. Os dois próximos gráficos, Figuras 4.22 e 4.23, mostram as senoides que somamos para obter a composta. Os asteriscos mostram os valores que seriam encontrados se as senoides fossem amostradas com a mesma temporização que a do sinal original. Observe como o número de senoides (8) obtidas a partir da transformada de Fourier corresponde ao número de pontos originais (8). Sim, a primeira senoide mostrada possui frequência zero. Você pode perceber que ele, o componente DC, equivale à média de todas as amostras do sinal original. A título de exercício, vamos aproximar o valor da penúltima amostra. Na Figura 4.22 percebemos que a amostra 6 de cada senoide possui os seguintes valores aproximados: 4.9, 0.3, −0.1 e 0.7. Se voltarmos nossa atenção para a Figura 4.23, veremos que os valores da penúltima amostra são aproximadamente: 0.4, 0.7, −0.1 e 0.3. Ao somarmos todos esses valores, obtemos 7.1. Se examinarmos novamente o original da Figura 4.21, veremos que o valor para a amostra 6 gira em torno de 7. Na verdade, a única razão para não obtermos exatamente o mesmo número que o da primeira figura seria o erro decorrente da análise puramente visual dos gráficos. Em um exercício semelhante, podemos anotar os valores máximos para cada uma das senoides nas Figuras 4.22 e 4.23. São eles: 4.88, 0.86, 0.76, 0.81, 0.38, 0.81, 0.76 e 0.86. Eles correspondem às amplitudes de cada uma das senoides, os valores ak de que precisaríamos para uma representação harmônica. As outras informações (além da frequência fundamental) seriam os ângulos de fase. Eles são um pouco 6 *

*

*

*

*

*

*

*

0

1

2

3

4

5

6

7

4 2 1

*

0 –1

* * *

0

1

1

Amplitude

2

* 4

0 *

0 0

*

* 1

5

2

* 3

4

7

* 5

*

* 2

6

* 7

*

*

1

6

*

1

–1

* * 3

*

0* –1

*

* 3

4

* 5

6

* 7

Amostra Figura 4.22

Weeks 004.indd 137

As primeiras quatro senoides no sinal composto.

04/04/12 19:07

138

CAPÍTULO 4

6

*

*

*

*

4 2

0

* 1

2

1 * 0 –1

0

1

Amplitude

1

4

1

* 3

4

6

*

2

* 3

4

* 5

6

* 7 *

*

0

* 7

*

* *

* –1

5

*

0

* 7

* * 2

* 1

6 *

*

*

0

* 5

*

0* –1

* 3

1

2

* 4

* 3

5

6

7

Amostra Figura 4.23 As últimas quatro senoides no sinal composto.

mais difíceis de ler a partir de um gráfico, mas podemos fazer aproximações. Lembramos que cos(0) = 1 e notamos que o valor obtido a partir da função cosseno diminui à medida que o ângulo aumenta, até alcançar −1 em ␲, e depois se eleva até alcançar novamente 1 em 2␲. Podemos utilizar essa observação para obter uma ideia preliminar sobre as fases. Se examinarmos o segundo gráfico na Figura 4.22, veremos que a senoide atinge seu ponto máximo exatamente na amostra 0. O ponto máximo corresponderia à amplitude, ou, mais exatamente, a1 × cos(2␲1f0t + ␾1) quando o argumento para a função cosseno é 0, tal como seria no instante de tempo 0. Desta forma teríamos 2␲1f00 + ␾1 = 0, ou simplesmente ␾1 = 0. Agora, precisamos encontrar o ângulo de fase seguinte, ␾2. Na amostra 0 (o terceiro gráfico na Figura 4.22), notamos que nosso valor seria aproximadamente 0.1. Ao encontrarmos a solução para ␾2, a2 × cos(2␲1f0t + ␾2) no instante de tempo 0 ⬇ 0.1. Isso significa que t = 0, de modo que a2 × cos(␾2) ⬇ 0.1, e já dispomos de uma aproximação de a2, como 0.8. Assim, podemos encontrar a solução:

Porém, existe um problema: como saber se a senoide aumentará no próximo instante de tempo? Para colocar essa questão de outra maneira, sabemos que a função cosseno retorna, para ␪, o mesmo valor que retornaria para 2␲ − ␪, então que valor devemos utilizar para nossa fase? Para resolvermos esse problema, recorremos à cadeira de cálculo (de uma forma bastante indolor!). Dispomos de uma representação gráfica para a senoide que desejamos e podemos simplesmente verificar visualmente no gráfico se ela aumenta ou diminui. No caso do terceiro gráfico da Figura 4.22, constatamos

Weeks 004.indd 138

04/04/12 19:07

Senoides

139

que ela aumenta. Se elevarmos ligeiramente o ângulo de fase, simulando um incremento de tempo bem pequeno, o valor resultante de a2 × cos((valor pequeno) + ␾2) deve ser pouco mais que a2 × cos(␾2). Vamos usar isso para testar nosso valor para ␾2.

Contudo isso mostra que a senoide diminuiria, ao contrário de nosso gráfico. Portanto, sabemos que o ângulo de fase deve ser uma versão negativada de nossa estimativa anterior, ou −46␲/100. Podemos (e devemos) conferir e reconferir nosso trabalho aplicando o mesmo truque conforme mostrado.

Constatamos que os valores seguintes aumentam, tal como observamos no gráfico. Podemos prosseguir dessa maneira para o resto dos gráficos. O programa a seguir demonstra o conceito de soma de senoides e não requer quaisquer parâmetros, por gerar o sinal original aleatoriamente. Esse programa encontra a representação X no domínio da frequência do sinal aleatório x e utiliza a informação no domínio da frequência para obter o sinal original de volta (na forma da lista my–sum–of–sins). Constatamos que, ao ser executado, o programa calcula o erro entre o sinal original e o reconstruído, um erro tão pequeno que se deve unicamente à precisão empregada. % sum_of_sins.m % Mostre um sinal aleatorio como uma soma de senos % % Gere nosso sinal x x = round(rand(1,8)*10); Xsize = length(x); % Obtenha FFT de x X = fft(x); Xmag = abs(X); Xphase = angle(X); % Mostre a informacao no dominio da frequencia como uma soma de senoides % Encontre o IFFT (o modo mais dificil) parte 1 n=0:Xsize-1; %m=0:Xsize-1; % Gere as senoides como pontos discretos somente s0 = Xmag(1)*cos(2*pi*0*n/Xsize + Xphase(1))/Xsize; s1 = Xmag(2)*cos(2*pi*1*n/Xsize + Xphase(2))/Xsize; s2 = Xmag(3)*cos(2*pi*2*n/Xsize + Xphase(3))/Xsize; s3 = Xmag(4)*cos(2*pi*3*n/Xsize + Xphase(4))/Xsize; s4 = Xmag(5)*cos(2*pi*4*n/Xsize + Xphase(5))/Xsize; s5 = Xmag(6)*cos(2*pi*5*n/Xsize + Xphase(6))/Xsize; s6 = Xmag(7)*cos(2*pi*6*n/Xsize + Xphase(7))/Xsize; s7 = Xmag(8)*cos(2*pi*7*n/Xsize + Xphase(8))/Xsize; % Gere novamente as senoides como curvas suaves t = 0:0.05:Xsize-1;

Weeks 004.indd 139

04/04/12 19:07

140

CAPÍTULO 4

smooth0 = Xmag(1)*cos(2*pi*0*t/Xsize + Xphase(1))/Xsize; smooth1 = Xmag(2)*cos(2*pi*1*t/Xsize + Xphase(2))/Xsize; smooth2 = Xmag(3)*cos(2*pi*2*t/Xsize + Xphase(3))/Xsize; smooth3 = Xmag(4)*cos(2*pi*3*t/Xsize + Xphase(4))/Xsize; smooth4 = Xmag(5)*cos(2*pi*4*t/Xsize + Xphase(5))/Xsize; smooth5 = Xmag(6)*cos(2*pi*5*t/Xsize + Xphase(6))/Xsize; smooth6 = Xmag(7)*cos(2*pi*6*t/Xsize + Xphase(7))/Xsize; smooth7 = Xmag(8)*cos(2*pi*7*t/Xsize + Xphase(8))/Xsize; % Encontre o IFFT (o modo mais dificil) parte 2 my_sum_of_sins = (s0+s1+s2+s3+s4+s5+s6+s7); smooth_sum = smooth0 + smooth1 + smooth2 + smooth3; smooth_sum = smooth_sum + smooth4 + smooth5 + smooth6 + smooth7;

% Mostre os pontos discretos e as curvas suaves juntos xaxis1 = (0:length(smooth0)-1)/20; % para 8 pontos xaxis2 = (0:length(s0)-1); figure(1); subplot(4,1,1); plot(xaxis1, smooth0, 'g', xaxis2, s0, subplot(4,1,2); plot(xaxis1, smooth1, 'g', xaxis2, s1, subplot(4,1,3); plot(xaxis1, smooth2, 'g', xaxis2, s2, subplot(4,1,4); plot(xaxis1, smooth3, 'g', xaxis2, s3, ylabel('amplitude'); xlabel('sample'); figure(2); subplot(4,1,1); plot(xaxis1, smooth4, 'g', xaxis2, s4, subplot(4,1,2); plot(xaxis1, smooth5, 'g', xaxis2, s5, subplot(4,1,3); plot(xaxis1, smooth6, 'g', xaxis2, s6, subplot(4,1,4); plot(xaxis1, smooth7, 'g', xaxis2, s7, ylabel('amplitude'); xlabel('sample');

'r*'); 'r*'); 'r*'); 'r*');

'r*'); 'r*'); 'r*'); 'r*');

% Mostre o original versus a reconstrucao a partir do dominio da frequencia figure(3); subplot(2,1,1); plot(0:Xsize-1, x, 'bd'); title('original'); min_y = floor(min(smooth_sum)); max_y = ceil(max(smooth_sum)); axis([0, Xsize-1, min_y, max_y]); % Melhore a aparencia dos eixos xaxis1 = (0:length(smooth_sum)-1)/20; xaxis2 = (0:length(s0)-1); subplot(2,1,2); plot(xaxis1, smooth_sum, 'b', xaxis2, my_sum_of_sins, 'rd'); title('sum of sinusoids'); Error = my_sum_of_sins - x Partimos do princípio de que o sinal contém somente informações reais. Em geral, esse não é o caso. Entretanto, uma vez que começamos com um sinal em que todos os valores são reais, terminaremos com um sinal com a mesma característica, já que não alteramos a informação – somente a transformamos do domínio do tempo para o domínio da frequência e depois a transformamos de volta. A transformada de Fourier resulta em um sinal complexo, embora o sinal original fosse real. Para conciliar isso, simplesmente tratamos o sinal real original como se ele fosse complexo, mas com a parte imaginária igual a zero. O sinal obtido a partir da transformada inversa também deve possuir uma parte imaginária igual a zero para todos os valores. De certa forma o programa executa a transformada inversa, mas não leva a parte complexa em consideração, tal como teria que fazer se o sinal original fosse complexo. Em outras palavras, o programa

Weeks 004.indd 140

04/04/12 19:07

Senoides

141

trata cada senoide como se ela fosse Xmag[k]cos(2␲kf0t + Xângulo[k]), quando na verdade cada senoide (a partir da fórmula de Euler) equivale a Xmag[k]cos(2␲kf0t + Xângulo[k]) + jXmag[k]sin(2␲kf0t + Xângulo[k]). Tal simplificação é possível por sabermos antecipadamente que as partes complexas, j sin(·), devem cancelar uma a outra. Representar um sinal que possua descontinuidades, tal como uma onda quadrada, com uma soma de senoides harmonicamente correlatas é uma tarefa difícil. Muitos termos são necessários [23]. Chamamos de fenômeno de Gibbs o fato de não podermos representar perfeitamente um sinal descontínuo como uma soma de senoides, mas nossa aproximação conterá sobressinal (overshoot), subsinal (undershoot) e ripples. A análise de Fourier funciona melhor com um sinal periódico. Nós implicitamente esperamos que a primeira e a última amostras estejam ligadas, de tal maneira que, se tivéssemos que coletar uma leitura a mais do sinal, esta seria igual à da primeira amostra, ou pelo menos um valor que já tivéssemos visto antes. Se esse valor N + 1 corresponde ou não ao valor 0 depende de quantas amostras tenhamos coletado. Além disso, consideramos que um número suficiente de amostras, dado pelo período de amostragem, tenha sido registrado. Se o sinal for verdadeiramente periódico, dispusermos de uma boa taxa de amostragem e tomarmos exatamente o equivalente a um período de amostras, então a amostra em N + 1 será igual à amostra 0. Às vezes não dispomos de um sinal periódico. Nesse caso, podemos ter uma descontinuidade entre a última amostra e a primeira. Considere, por exemplo, o sinal mostrado na Figura 4.24. Se tratarmos esse sinal como periódico, esperaremos que ele se repita indefinidamente e que possamos copiar e colar o sinal nele mesmo, tal como na Figura 4.25. Aqui vemos que existe uma descontinuidade no centro, e é difícil para uma transformada de Fourier lidar com ela (lembre-se do fenômeno de Gibbs). Portanto, devemos considerar que a análise de Fourier nos fornece uma aproximação do sinal original. E a nossa expectativa é que a aproximação não seja tão boa na região da descontinuidade quanto é para o resto do sinal. Chamamos essa dificuldade de efeitos de borda. Mesmo com os efeitos de borda, a análise de Fourier fornecerá uma boa aproximação do sinal original. Na verdade, qualquer sinal não aleatório pode ser aproximado por meio da análise de Fourier. Se tomarmos um sinal aleatório e encontrarmos sua série de Fourier, descobriremos que ele aproxima o sinal dos valores que examinamos. Entretanto, podemos ser tentados a usar isso para prever valores futuros do sinal. Se ele for verdadeiramente aleatório, contudo, qualquer correspondência entre o sinal e a previsão seria coincidência.

Figura 4.24 Um sinal curto.

Figura 4.25 O sinal curto, repetido.

Weeks 004.indd 141

04/04/12 19:07

142

4.17

CAPÍTULO 4

Espectro

Espectro é o nome atribuído ao traçado de frequência de um sinal. Ele consiste em um gráfico das frequências que compõem um sinal, com suas amplitudes exibidas umas em relação às outras. Um gráfico à parte exibe as fases para cada senoide. Anteriormente, na Figura 4.21, vimos que um sinal digital pode ser representado por uma soma de senoides. Essas senoides podem então ser graficamente representadas de forma individual, conforme mostrado nas Figuras 4.22 e 4.23, embora isso gere uma grande quantidade de informações que um espectro mostra de forma compacta. Às vezes, as informações sobre essa senoide proporcionam um discernimento sobre o conteúdo do sinal. Por exemplo, se o sinal original for o de uma música, as senoides nos informarão sobre as notas que estão sendo tocadas. Se o sinal original for o de uma pessoa tocando um violão ao ar livre, com um som agudo (tal como o apito de um trem) ao fundo, poderemos ser capazes de remover o componente de alta frequência (o apito) por meio de um filtro. Se prosseguirmos com o exemplo da última seção, veremos que uma informação faltante é a frequência fundamental. Conforme mencionado anteriormente, precisamos conhecer o intervalo de tempo entre as amostras antes de conhecermos as frequências. Mas faremos o melhor que pudermos sem isso. Vemos nas Figuras 4.22 e 4.23 que as senoides repetem um montante integral em cada gráfico, desde zero repetição (frequência zero) até sete repetições. Ou seja, as senoides se repetem com uma frequência tal que, se a amostra 8 fosse traçada, ela seria a mesma que a amostra 0. À medida que o número de repetições se eleva, o mesmo ocorre com a frequência correspondente. Suponha, para fins de argumentação, que o sinal original tenha sido amostrado a uma taxa de 125 milissegundos por amostra, de modo que lemos cada amostra 125 milissegundos após a última. O segundo gráfico, na Figura 4.22, mostra que a senoide se repete a cada 1000 milissegundos, ou uma vez por segundo (ela leva outros 125 milissegundos antes de ir para a amostra 8, que é idêntica à amostra 0). Portanto, sua frequência é de 1 Hz. Da mesma forma, vemos que o gráfico seguinte possui uma frequência de 2 Hz, depois de 3 Hz para o seguinte e assim por diante até 7 Hz para o último gráfico da Figura 4.23. Para o restante deste capítulo, consideraremos que dispomos desta frequência fundamental de 1 Hz. Se lembrarmos de nossas amplitudes {4.88, 0.86, 0.76, 0.81, 0.38, 0.81, 0.76, 0.86} da seção anterior e obtivermos as fases correspondentes em graus {0, 22, −81, −56, 0, 56, 81, −22}, poderemos traçar essas informações conforme mostrado na Figura 4.26. Na parte superior da figura, vemos um traçado da magnitude em frequência, uma vez que ele mostra as amplitudes positivas versus as frequências. A parte inferior da Figura 4.26 mostra um traçado de fase em frequência, pois ele mostra os ângulos de fase para cada frequência senoidal correspondente. Deve-se observar o padrão do espectro: para o traçado de magnitude em frequência, vemos uma imagem espelhada centralizada em torno de 4 Hz. No traçado da fase, novamente o padrão é centralizado

4.88

Amplitude

0.86 0.86 0.76 0.81 0.81 0.76 0.38 0

1

2

3

4

5

6

7

Frequência (Hz)

81 Fase (graus)

56 0 0

22 1

0 2

3 –56

4

5

6

7 Frequência (Hz) –22

–81 Figura 4.26

Weeks 004.indd 142

Espectro de magnitude em frequência e ângulos de fase.

04/04/12 19:07

Senoides

143

em torno de 4 Hz, mas temos uma imagem rebatida, além de espelhada. Imagine que a cortemos na amostra 4 e giremos a metade direita 180° em torno daquele ponto. Com essa orientação, o lado direito coincide com o lado esquerdo. Até aqui vimos como obter o espectro a partir das amplitudes e fases fornecidas pela transformada de Fourier. Para encontrar o espectro de um sinal abstrato, tal como um modelo matemático, utilize a fórmula inversa de Euler. Por exemplo, para encontrar e traçar o espectro de magnitude de x(t) = 2 + 2 cos(2␲(200)t), coloque-o em termos de funções cosseno.

Em seguida, utilize a fórmula inversa de Euler (Equação 4.2), repetida aqui: cos(␾) = (ej␾ + e−j␾)/2. Observe como isso resulta em dois componentes de frequência: um na frequência fornecida e outro na frequência negativa. Para o espectro, isso implica que a faixa de frequências será diferente do que vimos no exemplo anterior. Na prática, a informação permanece a mesma – apenas nosso traçado espectral possui valores de frequência diferentes, porém equivalentes. Isso é explicado no Capítulo 6, “A Transformada de Fourier”.

Isso resulta em uma magnitude 2 na frequência 0 Hz e uma magnitude 1 nas frequências 200 Hz e −200 Hz. Isso é mostrado no traçado de magnitude da Figura 4.27.

2 1

–300 Figura 4.27

–200

1

–100

0

100

200

300

Hz

Traçado do espectro: magnitude de x(t) = 2 + 2cos(2␲(200)t).

Exemplo 4.3 Para o sinal x(t) a seguir, x(t) = 4 cos(2␲ 100t + 3␲/4) + 2 cos(2␲ 200t) + 5 cos(2␲ 300t − 3␲/5) desenhe o espectro de magnitude e o espectro de fase desse sinal. Resposta:

Weeks 004.indd 143

04/04/12 19:07

144

CAPÍTULO 4

Se extrairmos as frequências {100, −100, 200, −200, 300, −300} e marcarmos as magnitudes correspondentes {2, 2, 1, 1, 2.5, 2.5}, obteremos o gráfico da Figura 4.28. Para cada frequência, também anotamos as fases, {3␲/4, −3␲/4, 0, 0, −3␲/5, 3␲/5}. Os ângulos de fase são mostrados na Figura 4.29. 2.5

2.5 2

2

1

–300

1

–200

–100

0

100

200

300

Hz

300

Hz

Figura 4.28 Traçado do espectro: magnitudes.

3␲ /4

3␲ /5 0 –300

–200

0 –100

–3 ␲ /4

0

100

200

–3␲ /5

Figura 4.29 Traçado do espectro: ângulos de fase.

RESUMO . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Este capítulo apresentou muitos conceitos importantes utilizados no processamento digital de sinais. Primeiramente, o de que qualquer informação pode ser representada por números. Qualquer número pode ser representado por um número complexo, ou seja, um número real é um número complexo com uma parte imaginária igual a zero. Podemos traçar valores complexos em um plano bidimensional. Pontos no espaço bidimensional podem ser imaginados como coordenadas cartesianas (x, y) ou como coordenadas polares (r ⬔ ␪), em que ␪ fornece a direção a partir da origem até o ponto em termos dos eixos real e imaginário. Valores complexos, como x + jy, trabalham com esse plano quando consideramos que o eixo y é o eixo j. Uma senoide é uma função cíclica que muda quando seu argumento muda. Com um argumento crescente (por exemplo, cos(2␲ft) à medida que t aumenta), a senoide rastreia em torno de um círculo como o valor x ou y em coordenadas cartesianas, por fim começando de novo. Uma senoide captura a mudança de valor de x (ou y) à medida que observamos as coordenadas (x, y) em torno de um círculo. O vetor (r ⬔ ␪) pode rastrear um círculo quando o ângulo ␪ (e, portanto, a frequência) aumenta (ou diminui). Uma frequência negativa significa simplesmente que rastreamos o círculo no sentido horário. Qualquer sinal (ou sequência de números) pode ser representado como uma soma de senoides. Qualquer senoide pode ser imaginada como a mudança de x e y à medida que rastreamos um círculo. Qualquer senoide pode ser representada como uma soma de dois fasores: o fasor (r/2 ⬔ ␪1) e o fasor (r/2 ⬔ ␪2), em que ␪1 = −␪2. Esses ângulos mudam ao longo do tempo (um aumenta enquanto o outro diminui). Portanto, qualquer sinal pode ser representado como uma soma de fasores que mudam ao longo do tempo. Os sinais do mundo real possuem componentes senoidais, a menos que sejam verdadeiramente aleatórios – e, nesse caso, ainda podem ser aproximados por uma soma de senoides. Visto que todos os sinais analógicos podem ser representados (ou ao menos aproximados) por uma soma de senoides, podemos visualizar um sinal em termos das frequências que o compõem. Dada uma equação de soma de senoides para um sinal, utilizamos a fórmula inversa de Euler para convertê-la em uma soma de exponenciais

Weeks 004.indd 144

04/04/12 19:07

Senoides

145

complexos, como (a1/2)e j2␲f1te j␾1 + (a1/2)e j2␲(−f1)te j(−␾1). A partir dessa forma podemos criar um traçado das amplitudes (a1/2, a1/2) versus as frequências (f1, −f1), bem como um traçado dos ângulos de fase versus as frequências. Juntas, chamamos essas informações de espectro, uma visualização do sinal no domínio da frequência. Veremos como obter as informações no domínio da frequência a partir de um sinal de pontos no domínio do tempo utilizando a transformada de Fourier, no Capítulo 6. Este capítulo fornece maiores detalhes sobre as senoides e abrange ideias fundamentais sobre e e j. Como vimos, podemos decompor um sinal em uma soma de muitas senoides. Aqui, temos a teoria básica por trás do tratamento de um sinal como uma soma de senoides e os modos pelos quais eles podem ser manipulados matematicamente. A transformada de Fourier consiste na decomposição de um sinal em seus componentes senoidais. Já a transformada inversa de Fourier consiste na recombinação dessas informações senoidais (amplitude, frequência e fase) para a recuperação do sinal original. Podemos visualizar as fases e magnitudes complexas a partir da transformada de Fourier como um segmento de linha girado e direcionado ou podemos visualizá-las como cossenos e senos complexos. A fórmula de Euler (e a fórmula inversa de Euler) nos permite ir de uma representação para a outra. Frequentemente, os cálculos são mais fáceis de efetuar em termos de amplitude × e j ângulo, embora desejemos visualizar os resultados como uma parte real (amplitude × cos(ângulo)) e uma parte imaginária (amplitude × sin(ângulo)).

EXERCÍCIOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1. Quais são os valores mínimo e máximo para x(t)? x(t) = 3 cos(2␲ 100t + ␲/6) 2. Dado que sin(␪) = cos(␪ − ␲/2), se x(t) = 3sin(2␲100t + ␲/6), o que é essa função em termos de cos? 3. O sinal analógico, x(t), é: x(t) = 3 cos(2␲ 2000t + ␲/4) + 2 cos(2␲ 5000t) + cos(2␲ 11000t − ␲/7) (a) Trace cada componente de frequência (no domínio do tempo) separadamente. (b) Represente essa função graficamente (no domínio do tempo). (c) Represente x(t) em termos de uma frequência fundamental, amplitudes e fases. 4. O sinal analógico, x(t), é: x(t) = 3 cos(2␲ 3000t) + cos(2␲ 1000t + ␲/3) + 4 cos(2␲ 5000t − ␲/3) Represente x(t) em termos de uma frequência fundamental, amplitudes e fases. 5. Qual é a diferença entre amplitude e magnitude? O que significa |x(t)|? 6. O componente DC especifica o “deslocamento do sinal” ______. (f = 0) O deslocamento de fase especifica o deslocamento de sinal ______. (␾) 7. Qual é o outro nome para um traçado da informação de frequência de um sinal? 8. Qual sinal é mais fácil de aproximar com uma série de senoides harmonicamente correlatas, uma onda quadrada ou uma onda triangular? Por quê? 9. Por que as amplitudes são sempre positivas? 10. Visto que x(t), como a seguir, encontra-se na forma ak cos(2␲(k)f0t + ␾k), quais são os valores para N, ak, ␾k e f0? Que nome especial atribuímos a esse sinal x(t)? x(t) = 4 + 3 cos(2␲(1)20t + ␲/2) + 7 cos(2␲(2)20t − 2␲/3)

Weeks 004.indd 145

04/04/12 19:07

146

CAPÍTULO 4

11. Crie seu próprio sinal harmônico (utilizando MATLAB) com no mínimo 10 senoides. Qual a equação para ele? Trace o gráfico para ele, tal como na Figura 4.20. 12. Se z = (2, 3), o que é z em termos de notação ej? 13. O que é 5e j ␲/4 em termos de coordenadas polares bidimensionais (raio e ângulo)? 14. É possível reduzir 6e j2␲ para um número real? 15. O que é x(t) = 4e j(2␲120t + ␲) em termos de seno e cosseno? 16. O que é 2e j3␲/8e j2␲100t em termos de seno e cosseno? 17. O que é {ae j(␻t+␾)}? 18. Considere o fasor girante e j ␻t+␾. O que ␻ nos informa sobre sua aparência? O que ␾ nos informa sobre sua aparência? 19. Se z = a + jb, o que é z*? 20. Qual o complexo conjugado de e j ␾? 21. Se x(t) = a × cos(k), em que k é uma constante, qual é a frequência? 22. Trace 4 + j5 em um gráfico e multiplique-o por j, traçando o resultado em seguida. Multiplique esse resultado por j novamente e indique-o no gráfico. Repita essa operação uma última vez. O que você observa? Que ângulos os quatro fasores possuem? 23. Considere z1 = 2e j 2 ␲/3 e z2 = e j ␲/7. O que é z3 = z1 + z2? Desenhe todos os três fasores em um gráfico. 24. Considere z1 = e−j ␲/5 e z2 = 3e j ␲/6. O que é z3 = z1 × z2? Desenhe todos os três fasores em um gráfico. 25. Considere z1 = 4e j(2␲100t − 5␲/4) e z2 = 5ej(2␲100t + ␲/6). O que é z3 = z1 + z2? Desenhe todos os três fasores em um gráfico nos instantes de tempo {0 seg., 0.1 seg. e 0.2 seg.}. 26. Vimos que podemos somar fasores girantes quando eles giram na mesma frequência. O que acontecerá se os somarmos quando suas frequências não coincidirem? Utilize o MATLAB para encontrar (ponto a ponto) e traçar z3 = z1 + z2, em que z1 = 4e j(2␲ 25t − 5␲/4) e z2 = 5e j(2␲ 33t + ␲/6). Considere que t varia de 0 a 1 com um pequeno incremento apropriado.

PROJETO . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1. Mostre um sinal aleatório como uma soma de senoides. Com base em sum_of_sins.m, crie o sinal ou extensão arbitrária (N) e permita que os números aleatórios variem entre −100 e 100. Um exemplo de solução é fornecido no site da LTC Editora, como project4.m. 2. Imagine que você tenha contraído um empréstimo de US$ 200000 para adquirir uma casa, a uma taxa de juros de 6.25%, e concorde em quitá-lo em prestações mensais durante 30 anos. Com juros compostos, se você pagar US$ 1500 por mês, quanto tempo levará para pagar o empréstimo? Um exemplo de solução é fornecido no site da LTC Editora, como project4b.m. Tente resolver esses problemas por conta própria, antes de ler o texto a seguir.

Exibição de um sinal como uma soma de senoides Em primeiro lugar, precisamos criar nosso sinal de exemplo, literalmente um aleatório. Definimos o número de pontos, N, e utilizamos em seguida a função read para obtermos N valores. O código a seguir mostra isso, bem como algum escalonamento nos números aleatórios.

Weeks 004.indd 146

04/04/12 19:07

Senoides

147

N = 100; % numero de pontos em nosso sinal x = round(100-rand(1,N)*200); A variável de lista x refere-se ao sinal de exemplo. Em seguida, encontramos a FFT de x e a decompomos em magnitudes (Xmag) e ângulos de fase (Xphase). Precisaremos saber a extensão do sinal original para podermos atribuí-la a uma variável (Xsize) também. X = fft(x); Xmag = abs(X); Xphase = angle(X); Xsize = length(x); Depois, encontramos as senoides a partir dessa informação. Elas serão curvas suaves, o que significa que mostramos a ascensão e queda de uma senoide em vez de somente os pontos discretos de que necessitamos. t = 0:0.05:Xsize-1; for k=1:N smooth(k,:) = Xmag(k)*cos(2*pi*(k-1)*t/Xsize + Xphase(k))/Xsize; end Estamos executando nossa própria versão da transformada inversa de Fourier. O código anterior gerou as ondas cossenoidais que, quando somadas, produzem uma onda que passa pelos pontos discretos do sinal original. Rotulamos essas ondas suaves como smooth, uma matriz em que cada linha corresponde a uma senoide. Em seguida, somamos todas as ondas cossenoidais e armazenamos o resultado na lista smooth_ sum. smooth_sum = smooth(1,:); for k=2:N smooth_sum = smooth_sum + smooth(k,:); end O código anterior mostra que inicializamos smooth_sum para a primeira onda cossenoidal e depois somamos as ondas subsequentes a ela. O resultado final tem aparência suave quando o traçamos, olhando apenas uma parte dele por vez. Ele intencionalmente possui aproximadamente 2000 pontos, ou 20 pontos de suavização para cada ponto discreto original. Podemos traçar essa soma de senoides com o sinal original e ver a onda suave passar em cada ponto discreto do original. Você pode notar na solução project4.m que a matriz s e a lista my_sum_of_sins também são produzidas de forma similar. Elas possuem apenas o número mínimo de pontos. O resultado final, my_sum_of_ sins, deve ser exatamente o mesmo que o do sinal original. O código a seguir demonstra isso. >> Error = sum(abs(my_sum_of_sins - x)) Error = 6.1389e-11 Quando calculamos o erro, descobrimos que ele é desprezível. Reembolso do empréstimo Para calcularmos o reembolso do empréstimo, precisamos definir os parâmetros. Isso equivale a extrair a informação do enunciado do problema e atribuí-la a variáveis com nomes apropriados. interest_rate = 0.0625; principal = 200000;

Weeks 004.indd 147

% 6.25 por cento

04/04/12 19:07

148

CAPÍTULO 4

years = 30; monthly_payment = 1500; n = 10000; compound = principal; Utilizamos n para representar o número de vezes que o empréstimo é composto. Se ele fosse infinito, teríamos juros compostos continuamente. A utilização de um número elevado para n nos fornece uma estimativa razoavelmente próxima dos juros compostos continuamente. Além disso, inicializamos o montante compound como o principal do empréstimo. Em seguida, configuramos um par de loops aninhados. O loop while externo conta os anos até aquele especificado previamente, enquanto o loop while interno conta os doze meses em um ano. Em cada mês, efetuamos o pagamento mensal especificado, de modo que o programa deduza isso do montante composto. Depois, compomos os juros no montante restante. yr = 1; % ano corrente % count the years while ((yr < years)&&(compound > 0)) mo = 1; % mes corrente % count the months while ((mo < 12)&&(compound > 0)) % a cada mes deduzimos o pagamento compound = compound - monthly_payment; % componha os juros continuamente for i=1:n compound = compound*(1+ interest_rate/(n*12)); end mo = mo + 1; % atualize nosso mes end yr = yr + 1;

% atualize nosso ano

end O loop for compõe os juros. Utilizamos um número grande (mas não infinito) de cálculos. Os loops while são úteis aqui por permitirem que o processamento seja interrompido tão logo o montante que resta a ser pago, compound, alcance ou ultrapasse zero dólar.

Weeks 004.indd 148

04/04/12 19:07

Amostragem

5

N

ós vivemos em um mundo analógico, no qual os sinais que nos interessam são tipicamente contínuos. Nossos computadores são dispositivos digitais, o que significa que eles podem armazenar exclusivamente dados discretos e finitos, limitados tanto em precisão quanto em magnitude. Portanto, precisamos aproximar os sinais antes de poder armazená-los e manipulá-los. Como preencher a lacuna entre o mundo real e o mundo sintético de nossos computadores? Este capítulo trata dessa questão fundamental. Amostragem é o processo de obtenção de um sinal digital a partir de um analógico. Quando amostramos um sinal, nós registramos, de vez em quando, um valor. A amostragem pode resultar em muitos valores de dados. Uma música de 3 minutos, por exemplo, amostrada a uma taxa de 16 bits a 44100 Hz (para dois canais), produz:

Se mudarmos as unidades para torná-las uniformes, teremos:

Mas nós realmente precisamos armazenar 30 MB de dados para uma música de 3 minutos? Na prática, existem outras formas de se armazenar tais dados. O formato MP3 é uma solução desse tipo. Sua popularidade se deve ao fato de uma música armazenada como um arquivo MP3 soar de forma muito próxima à original, embora só requeira aproximadamente 10% do espaço.

5.1

Amostragem

A conversão de um sinal analógico em um digital constitui uma etapa necessária para que um computador consiga analisar um sinal: os computadores modernos são máquinas digitais e podem armazenar exclusivamente valores digitais. O período de um sinal é o intervalo de tempo antes que ele se repita. Para uma senoide, precisamos conhecer somente a frequência f, pois definimos o período como T = 1/f. Nós modelamos a amostragem substituindo o índice contínuo t de uma função contínua x(t), por nTs, um valor discreto. Utilizamos n para indexar a lista discreta, e Ts é o período de amostragem, o intervalo de tempo entre duas amostras. O tempo de amostragem é o inverso da frequência de amostragem fs, ou seja, Ts = 1/fs. A equação x[n] = x(nTs) descreve o processo de amostragem. Isso significa que o sinal contínuo x é convertido em uma representação digital. x[n] não é exatamente o mesmo que x(t). Isso não é possível, pois x(t) é um sinal contínuo. Na melhor das hipóteses, x[n] é uma boa aproximação de x(t). Antes de demonstrarmos a amostragem, vamos definir a faixa de frequência do sinal em que estamos interessados, a largura de banda. Por vezes simplesmente utilizamos a frequência mais alta como a largura de banda, indicando que estamos interessados em todas as frequências entre 0 Hz e a máxima. Em outras vezes, ela pode ser limitada – por exemplo, o espectro de luz visível. O termo amostragem crítica aplica-se ao caso em que a taxa de amostragem equivale exatamente ao dobro da largura de banda, B. Isso significa que registraremos o sinal com rapidez suficiente para 149

Weeks 005.indd 149

13/04/12 07:00

150

CAPÍTULO 5

reconstruí-lo de forma apropriada posteriormente, ou seja, fs = 2B. Obtemos esse valor com base no critério de Nyquist, fs > − 2B, e escolhendo a frequência de amostragem mais baixa possível. Chamamos de sobreamostragem (oversampling) a prática de coleta de amostras com maior frequência do que o necessário (ou mais do que duas vezes a largura de banda). Isso resulta em dados redundantes. Embora a sobreamostragem seja possível, ela pode não ser desejável devido às limitações de hardware, como a incapacidade de processar o sinal com rapidez suficiente ou de armazená-lo em memória. A sobreamostragem pode ser algo desejável se você estiver trabalhando com hardware de baixo custo, tal como um conversor digital-analógico* econômico, o que parece ser o caso em relação aos leitores de CD [7]. Um conversor digital-analógico de baixo custo pode utilizar uma função degrau simples para recriar o sinal. Desse modo, quanto mais amostras ele tiver para trabalhar, menor será o tempo entre as amostras e melhor será a aproximação em relação ao sinal original. Para citarmos um contraexemplo, a leitura (e o armazenamento) da temperatura a cada microssegundo produziria um volume gigantesco de dados e nem por isso favoreceria a previsão de tempo para o dia seguinte. A subamostragem (undersampling) ocorre quando não coletamos amostras o suficiente ou menos do que duas vezes a largura de banda. Normalmente ela não é desejável, pois não é possível reconstruir o sinal. Além disso, qualquer análise feita em um sinal subamostrado provavelmente será errônea (como costumamos dizer em programação computacional, “lixo que entra, lixo que sai”). Não há exceção para esse caso. Os pesquisadores estão experimentando a amostragem compressiva (também chamada de sensoriamento compressivo), no qual um sistema coleta amostras aleatoriamente utilizando uma taxa baixa [24]. Em sistemas que comprimem dados, tal prática poderia funcionar com mais eficiência do que outra que lê dados redundantes somente para descartar grande parte deles mais tarde. Para simular a amostragem de um sinal contínuo, utilizamos um código como este a seguir. N = 100; Ts = 1/500; n = 0 : N-1; x = cos(2*pi*45*n*Ts); stem(n, x) Aqui, definimos o número de amostras a serem lidas, N, para um total de 100 amostras. Escolhemos um período de amostragem, Ts, correspondendo a 2 milissegundos ou 500 amostras por segundo. Em seguida, configuramos nosso índice (n) para que varie de zero até a última amostra a ser lida. Quando o computador encontrar a função cosseno, expandirá o argumento para uma lista 245Ts para cada n. Portanto, a variável x também será uma lista, contendo amostras simuladas de uma senoide de 45 Hz. O comando final, stem, exibe as amostras simuladas sem conectá-las.

Exemplo 5.1 Se utilizássemos o sinal x(t) abaixo e o amostrássemos a uma taxa de 20 kHz, quantas amostras teríamos após 60 milissegundos? x(t) = 3 cos(2 404t + /4) + 2 cos(2 6510) + cos(2 660t − /5) Resposta: A primeira coisa a se observar é que o número de amostras efetivamente não depende do sinal propriamente dito. Ou seja, tudo de que precisamos para responder a essa pergunta é a taxa de amostragem de 20 kHz (fs) e o tempo total, 60 milissegundos. O período de amostragem é de 1/(20 kHz) ou 1/(20000 Hz). Visto que Hz equivale a ciclos/segundo, 1/Hz equivaleria a segundos/ciclos, ou simplesmente segundos. Coletamos a primeira amostra no instante de tempo = 0, depois aguardamos 1/(20000) segundo e coletamos outra e prosseguimos até atingirmos 60 milissegundos = 60 × 10−3 segundo = 0.060 segundo. Para encontrar a resposta numérica, multiplique 0.060 por 20000 e some 1 (uma vez que a primeira amostra foi coletada no instante 0). Para responder a essa (ou qualquer) pergunta em um teste, é importante mostrar seu processo mental. Uma boa resposta incluiria a análise (e o texto) como a anterior.

* Mais conhecido como conversor D/A. (N.T.)

Weeks 005.indd 150

13/04/12 07:00

Amostragem

151

Exemplo 5.2 Se x(t) = 4 cos(2250t + 2/7), qual o período dessa senoide? Resposta: Tudo de que realmente precisamos é a frequência, 250 Hz. T = 1/f = 1/250 Hz = 0.004 segundo.

Suponha que tenhamos um sinal complexo, com muitas senoides harmonicamente correlatas. Para encontrarmos o período, precisamos descobrir a frequência maior, f0, de tal forma que todas as demais frequências sejam múltiplos inteiros dela. Com duas ou mais senoides, essa frequência base pode ser encontrada com a função máximo divisor comum (gcd*). Para três frequências f1, f2 e f3, precisamos encontrar f0 de tal forma que f1 = k1f0, f2 = k2f0 e f3 = k3f0. As variáveis k1, k2 e k3 devem ser inteiros.

5.2

Reconstrução

A Reconstrução é o processo de recriação de um sinal analógico a partir de amostras digitais. Essa conversão de digital em analógico envolve interpolação, ou “preenchimento” das informações faltantes entre amostras. Existem diversas maneiras de se fazer isso, sendo a mais óbvia um método linear de “ligação de pontos” desenhando segmentos de linha entre as amostras. Um conversor analógico-digital,** abreviado para ADC, amostra um sinal contínuo e produz um discreto. O oposto do ADC é o DAC, ou conversor digital-analógico. Quando um DAC produz um sinal analógico entre pontos de dados discretos, chamamos esse processo de interpolação, assim como é possível interpolar ou preencher os valores entre pontos de dados. Um modo simples de fazer isso consiste em utilizar um retentor de ordem zero*** para uma função pulso [25]; isso gera uma função degrau de tal modo que o tempo entre duas amostras é preenchido pela primeira dentre as duas amostras. Outra opção para a interpolação é a interpolação linear, na qual a função pulso é baseada na variável t. Um exemplo disso é uma função triângulo, na qual o pulso sobe (na forma de rampa) até o ponto médio e desce novamente em seguida. A equação a seguir descreve o processo de conversão digital-analógica (DAC) [7]:

em que p(t) é a função pulso. Ela pode ser simplesmente uma versão prolongada da função impulso unitário. Na verdade, podemos utilizar uma equação muito semelhante com a função impulso para descrever a amostragem, conforme foi feito em [15]. Naturalmente, o índice não vai de − a , mas essa é a forma mais geral de se definir isso sem conhecer o suporte.

5.3

Amostragem e o Ruído de Alta Frequência

Durante a amostragem de um sinal com ruído de alta frequência, veremos os efeitos do ruído mesmo quando a frequência de amostragem for menor que o ruído. A razão disso é que o ruído tornará cada amostra um pouco maior ou menor do que ela seria de outra forma. A Figura 5.1 demonstra essa ideia. São exibidas amostras de um sinal com algum ruído de alta frequência, juntamente com o sinal original (sem ruído) na forma de uma linha sólida. Uma sobreposição espectral do ruído aparecerá no sinal amostrado como um componente de frequência, conforme explicado na próxima seção.

* greatest common divisor ou MDC em português. (N.T.) ** Mais conhecido como conversor A/D. (N.T.) *** zero order hold (ZOH), também chamado de reconstrutor de ordem zero. (N.T.)

Weeks 005.indd 151

13/04/12 07:00

152

CAPÍTULO 5

cos(2 20t + ) + 0.6 cos(2  31t  /7) + ruído

1.5

1

0.5

0

–0.5

–1

–1.5

–2 0

0.01

0.02 0.03 0.04 0.05 0.06 0.07 0.08 0.09 Sinal original (linha sólida), sinal + amostras de ruído (pontos)

0.1

Figura 5.1 Amostragem de um sinal ruidoso.

5.4

Sobreposição Espectral

Um problema comum que surge durante a amostragem de um sinal contínuo é a sobreposição espectral, na qual um sinal amostrado possui réplicas de seus componentes senoidais, o que pode interferir em outros componentes. A sobreposição espectral ocorre durante a amostragem de um sinal periódico, a saber, uma senoide (ou um sinal composto de senoides) tal como a × cos(2ft + ). Quando amostramos esse sinal, o fazemos à taxa fs. Qualquer componente senoidal acima de fs aparecerá no sinal como se fosse inferior a fs. Isso pode ser mais bem explicado com um exemplo. Suponha que tenhamos um sinal senoidal x(t) = cos(2800t). Quando o amostramos, substituímos t no sinal contínuo x(t) por pontos nTs espaçados regularmente no tempo, ou seja, x[n] = x(nT s). Suponha que amostremos essa senoide de 800 Hz a uma taxa fs = 600 amostras/segundo. Ao examinarmos o conteúdo de frequência do sinal amostrado, constatamos que ele aparece como uma senoide de 200 Hz. O que aconteceu? Em vez da senoide de 800 Hz, encontramos uma de 200 Hz. Vamos examinar o que é o sinal amostrado: x(t) = cos(2 800t) x[n] = cos(2 800nT s) Agora substituímos 800 por 200 + 600. A razão para tal ficará clara em breve. x[n] = cos(2 (200 + 600)nT s) x[n] = cos(2 200nT s + 2 600nT s)

Weeks 005.indd 152

13/04/12 07:00

Amostragem

153

Ts = 1/fs e, portanto, Ts = 1/600. Substituiremos isso no termo mais à direita. x[n] = cos(2 200nT s + 2 600n(1/600)) x[n] = cos(2 200nT s + 2 n) A função cosseno é periódica com período 2, o que significa que cos( + 2) = cos(). Assim como podemos somar 2 ao argumento do cosseno e obter o mesmo resultado, podemos somar 2 vezes qualquer inteiro e obter o mesmo resultado (mesmo que o inteiro seja negativo). Definimos o índice n como um inteiro, de modo que o termo 2n pode ser removido do argumento do cosseno anterior, deixando-nos com cos(2200nTs). Quando amostramos uma senoide de 800 Hz à taxa de 600 amostras/segundo, não conseguimos distingui-la de uma senoide de 200 Hz. Se também tivéssemos um componente de 200 Hz no sinal, ele sofreria interferência do componente de 800 Hz. A sobreposição espectral ocorre na TV sempre que vemos na tela um carro cujas rodas parecem estar girando no sentido errado. Uma transmissão televisiva pode ser encarada como uma série de imagens, amostradas a uma taxa regular, surgindo na tela. Se por acaso as rodas girarem menos de um círculo completo entre quadros (imagens), elas parecerão estar girando lentamente no sentido oposto. Isso pode ser generalizado como se segue. Imagine que a frequência de um componente senoidal seja substituída pela frequência mais um múltiplo inteiro da frequência de amostragem, ou seja, substituímos f por f0 + kfs. x(t) = a × cos(2 (f0 + kfs)t + ) x(t) = a × cos(2 f0t + 2 kfst + ) Agora amostramos esse sinal em intervalos Ts. Note que não há problemas com a senoide até que a amostremos. x[n] = x(nTs) = a × cos(2 f0nTs + 2 kfsnTs + ) Visto que Ts = 1/fs, podemos eliminar o termo fsTs. x[n] = x(nTs) = a × cos(2 f0nTs + 2 kn + ) Tanto k quanto n são inteiros – portanto seu produto também é um inteiro. Isso significa que: a × cos(2 f0nTs + 2 kn + ) = a × cos(2 f0nTs + ). Em outras palavras, uma vez que amostremos um sinal, não podemos distinguir uma senoide nele (tal como 800 Hz no exemplo anterior) a partir de outra senoide (tal como 200 Hz) que contenha um múltiplo inteiro da frequência de amostragem, nesse caso 600 Hz. Claramente, isso significa que a frequência de amostragem fs deve ser no mínimo maior do que o componente de maior frequência, considerando-se que temos conteúdo espectral começando em 0 Hz. Examinaremos outras restrições pertinentes a fs nas próximas seções. A sobreposição espectral significa que, se examinarmos o espectro de uma senoide de f Hz, haverá um componente em f, mas também haverá componentes em (f + 1fs), depois em (f + 2fs), depois em (f + 3fs) etc. Portanto, mostramos somente o espectro para uma faixa de fs ou menos, pois ela se repete depois disso. A visualização a partir de 0 a fs, ou, como alternativa, a partir de −fs/2 a +fs/2, revela toda a informação. Para sinais reais, muitas vezes mostramos somente a faixa de 0 a fs/2.

5.4.1

Exemplo de Sobreposição Espectral

O programa a seguir demonstra graficamente esse problema. Simulamos a amostragem de duas senoides, 2 cos(2100t + /3) e 2 cos(2600t + /3), a uma taxa de 500 amostras/segundo. São atribuídas a elas a mesma magnitude e fase por uma razão: o programa mostra que as versões amostradas são idênticas.

Weeks 005.indd 153

13/04/12 07:00

154

CAPÍTULO 5

% % % % % % % %

Exemplo de sobreposicao espectral com 2 cos(2 pi 100 t + pi/3) e 2 cos(2 pi 600 t + pi/3) se forem amostrados a uma taxa de 500 amostras/seg. entao as versoes amostradas sao identicas!

freq = 100; phase = pi/3; mag = 2; fs = 500; Ts = 1/fs; k = 1;

% % % % % %

num_points = 200;

frequencia de exemplo fase de exemplo magnitude de exemplo frequencia de amostragem periodo de amostragem numero de repeticoes

num_samples = 11;

% O numero de pontos a utilizar % 200 pontos tornam sua aparencia suave % O numero de amostras para simular a leitura % 11 amostras colocam “amostrado” sob analogico

step = 2/(freq*num_points); t = 0:step:2*(1/freq); n = 0:num_samples-1;

% obtenha um incremento apropriado % “tempo” % indice de amostra

% x e y sao funcoes analogicas simuladas x = mag*cos(2*pi*freq*t + phase); y = mag*cos(2*pi*(freq+k*fs)*t + phase); % x2 e y2 sao versoes amostradas de x e y x2(n+1) = mag*cos(2*pi*freq*n*Ts + phase); y2(n+1) = mag*cos(2*pi*(freq+k*fs)*n*Ts + phase); É fácil perceber quando se devem coletar amostras. Uma vez que a frequência de amostragem é de 500 amostras/segundo, o período de amostragem deve ser de 1/500 = 0.002 segundo. Se coletarmos nossa primeira amostra no instante de tempo t = 0, as próximas coletas ocorrerão em t = 0.002 segundo, 0.004 segundo, 0.006 segundo e assim por diante. Tanto x2 quanto y2 possuem os mesmos valores. Para exibir as versões amostradas simuladas, o código a seguir traça ambas e gera a Figura 5.2. % Trace os sinais ”analogicos” subplot(2,1,1); plot(t, x, 'r.-', t, y,'b-'); my_title = sprintf('Sinais analogicos simulados, x=pontos y=solido'); title(my_title); xlabel(' tempo '); ylabel('Amplitude'); % Trace os sinais ”amostrados” subplot(2,1,2); plot(n,x2,'rx', n,y2,'bo'); my_title = sprintf('Sinais digitais simulados, x=x y=o'); title(my_title); xlabel(' amostras '); ylabel('Amplitude');

Weeks 005.indd 154

13/04/12 07:00

Amostragem

155

Sinais analógicos simulados, x = pontos y = sólido

2

Amplitude

1 0 –1 –2

0

0.002

0.004

0.006

0.008

0.01 0.012 Tempo

0.014

0.016

0.018

0.02

8

9

10

Sinais digitais simulados, x = x y = o 2

Amplitude

1 0 –1 –2

0

1

2

3

4

5 6 Amostras

7

Figura 5.2 A sobreposição espectral demonstrada.

A Figura 5.2 mostra ambas as senoides, 100 Hz e 600 Hz, na parte superior, isto é, antes da amostragem. A parte inferior mostra as amostras simuladas de cada uma como xs e os. Para cada amostra da onda de 100 Hz, observamos que a senoide de 600 Hz nos fornece exatamente o mesmo resultado. A parte superior mostra como os sinais analógicos coincidentemente se cruzam exatamente onde as amostras são lidas.

5.4.2

Dobramento

Vimos que qualquer frequência maior do que a frequência de amostragem aparecerá como uma frequência mais baixa. O dobramento (folding) é outra forma de encarar o fenômeno da sobreposição espectral, quando a frequência observada de um sinal amostrado difere da frequência real, muito embora ela seja inferior à frequência de amostragem porém maior do que metade dela. Alguns textos empregam o termo dobramento [2, 7, 12], enquanto outros o mencionam exclusivamente como sobreposição espectral [10, 25, 26]. Trata-se de um tipo de sobreposição espectral, mas aqui preferimos o termo dobramento para distingui-lo da sobreposição espectral, visto que o ângulo de fase é afetado. Você também pode ver fs/2 aludida como a frequência de dobramento caso, por exemplo, uma senoide de 600 Hz seja amostrada à taxa de fs = 1000 amostras/segundo e a frequência observada seja de 1000 − 600 = 400 Hz. Isso advém do fato de que:

Tal como antes, podemos remover um múltiplo inteiro de 2 da função cosseno. Visto que n é definido como um inteiro, podemos remover o termo 2n: cos(2n − 2 400nTs + ) = cos(−2 400nTs + ).

Weeks 005.indd 155

13/04/12 07:00

156

CAPÍTULO 5

Pode parecer estranha a presença de um argumento negativo para a função cosseno, mas existe uma identidade trigonométrica que pode ajudar: cos(−) = cos(). Se aplicarmos isso à equação anterior, teremos: cos(−2 400nTs + ) = cos(2 400nTs − ). A frequência agora parece ser de 400 Hz, mas o valor do ângulo de fase  é negativado. Desse modo, se tivéssemos que traçar a resposta de magnitude em frequência para esse sinal x(t) = cos(2600n/1000 + ), veríamos uma frequência de 400 Hz. Se traçássemos os ângulos de fase, ela apareceria como −. O código a seguir mostra que a função cosseno fornece os mesmos resultados, seja o argumento positivo ou negativo. A Figura 5.3 mostra o resultado desse código, com cos(−210t − /3) na metade esquerda da figura e cos(210t + /3) na metade direita. % % Demonstre que cos(–teta) = cos(teta) % t=-0.1:0.001:0.1; phi = pi/3; freq = 10;

% tempo % fase de exemplo % frequencia de exemplo

x = cos(2 * pi * -freq * t - phi); y = cos(2 * pi * freq * t + phi); t1 = -0.1:0.001:0; t2 = 0:0.001:0.1; %plot(t,x,'b.', t, y, 'g*')

% Este tracado mostra ambos se sobrepondo

plot(t1,x(1:ceil(length(x)/2)),'b*', t2, ... y(ceil(length(y)/2):length(y)), 'gd') axis([-0.1 0.1 -1 1]) title('cos(–teta) e cos(teta) produzem os mesmos resultados'); xlabel('cos(-2\pi f t-\phi) (.) and cos(2\pi f t+\phi) (*)')

cos(−) e cos() produzem o mesmo resultado ********* ** *** * ** * ** 0.8 ** * * * * * * * 0.6 * * * * * * * * 0.4 * * * * * * * 0.2 * * * * * * 0 * * * * * * * – 0.2 * * * * * * – 0.4 * * * * * * * * – 0.6 * * * * * * * * – 0.8 * ** ** ** * *** *** –1 ******* –0.1 –0.08 –0.06 –0.04 –0.02 0 0.02 0.04 0.06 cos(2 ft   ) (esquerda*) e cos(2 ft + ) (direita) 1

Weeks 005.indd 156

0.08

0.1

Figura 5.3 cos(−210t − /3) e cos(210t + /3) produzem o mesmo resultado.

13/04/12 07:00

Amostragem

157

Um comando plot alternativo aparece como um comentário dentro do código. Ele mostra que duas funções cosseno se sobrepõem. Se você decidir experimentá-lo, não se esqueça de inserir o sinal de comentário no outro comando plot. O dobramento ocorre quando o sinal amostrado possui um componente senoidal maior do que fs/2, porém menor do que fs. Em geral, podemos substituir uma frequência f por (fs − f0) para fornecer:

O valor de f0 deve ser inferior ao do f original – caso contrário a frequência dobrada estará fora do espectro. Por exemplo, se começássemos com cos(2400nTs + ), com Ts = 1000 Hz, e substituíssemos 400 por 1000 − 600, terminaríamos com cos(2600nTs − ). Isso não apareceria no espectro (de −fs/2 a fs/2), exceto como cos(2400nTs + ), que é precisamente o que tínhamos no começo.

5.4.3

Localização de Replicações após Amostragem

Uma vez que um sinal seja amostrado, um número infinito de replicações de componentes de frequência aparecerá em intervalos regulares. Isso é baseado na periodicidade das senoides, ou seja, cos(2ft) = cos(2ft + 2n), em que n é um inteiro, tal como cos(/6) = cos(/6 + 2) = cos(/6 + 22). Isso significa que a amostragem de um sinal colocará todo conteúdo de frequência na faixa de 0 a fs, ou, como alternativa, entre −fs/2 e + fs/2. Visto que n e k são ambos inteiros por definição, seu produto nk também é um inteiro. cos(2 f1nTs) = cos(2 f1nTs + 2 nk) = cos(2 n(f1Ts + k)) Podemos substituir Ts por 1/fs. Além disso, como fs/fs = 1, podemos introduzir isso à frente do termo k.

Portanto, cos(2f1nTs) é indistinguível de cos(2(f1 + kfs)nTs), para todos os k inteiros. Desse modo, durante a amostragem à taxa de fs amostras por segundo, cos(2f1t) é indistinguível de cos(2(f1 + kfs)t), o que significa que a amostragem gera uma réplica de uma frequência a cada múltiplo inteiro da frequência de amostragem mais aquela frequência. Examinaremos os valores negativos de k em seguida. A função cosseno traduz-se em dois fasores com a fórmula inversa de Euler. Seja o ângulo positivo ou negativo, o mesmo resultado aparece no espectro. Em outras palavras,

aparece no espectro em +f1 e −f1, respectivamente. Se negativarmos o argumento, obteremos

Weeks 005.indd 157

13/04/12 07:00

158

CAPÍTULO 5

o qual ainda aparece no espectro em +f1 e −f1. Isso significa que todo conteúdo de frequência real no espectro entre 0 e +1 será espelhado entre −1 e 0. Portanto, uma frequência amostrada de f1 Hz também aparecerá em −f1 Hz. Quando combinada com a sobreposição espectral, constatamos que ela também aparece entre fs/2 e fs. Suponha que consideremos k = −1:

Portanto, cos(2f1nTs) também aparece como cos(2(f1 − fs)nT s). Exemplo 5.3 Considere f1 = 1 Hz e fs = 4 amostras/segundo: cos(2 (1)nT s) = cos(2 n(1 − 4)T s) = cos(2 n(−3)T s) = cos(2 n(3)T s). Veja a Figura 5.4. Observe que teríamos uma réplica em −1 Hz. Como existe um número infinito de replicações, mostraremos somente algumas, embora haja replicações em ±5 Hz e ±7 Hz.

–8 –7 –6 –5 –4 –3 –2 –1 –fs –fs /2 Figura 5.4

0

1 f1

2 3 fs /2

4 fs

5

6

7

8

Hz

Replicações para f1 = 1 Hz e fs = 4 amostras/segundo.

Exemplo 5.4 Considere f1 = 2.5 Hz e fs = 8 amostras/segundo: cos(2 (2.5)nT s) = cos(2 n(2.5 − 8)T s) = cos(2 n(−5.5)T s) = cos(2 n(5.5)T s). Veja a Figura 5.5. Observe que teríamos uma réplica em −2.5 Hz.

–8 –7 –6 –5 –4 –3 –2 –1 –fs –fs /2 Figura 5.5

0

1

2

3 f1

4 5 fs /2

6

7

8 Hz fs

Replicações para f1 = 2.5 Hz e fs = 8 amostras/segundo.

A partir desses exemplos, vemos que cos(2 f1nT s) parece o mesmo que cos(±2 (f1 − fs)nT s), ao menos quando f1 < fs/2.

Weeks 005.indd 158

13/04/12 07:00

Amostragem

159

Exemplo 5.5 O que acontece se f1 > fs/2? Com k = −1, consideraremos f1 = 3 Hz e fs = 4 amostras/ segundo: cos(2 (3 − 4)nT s) = cos(2 (−1)nT s) = cos(2 nT s). Veja a Figura 5.6. Note que também teríamos uma réplica em −3 Hz e outras além de ±fs.

–8 –7 –6 –5 –4 –3 –2 –1 –fs –fs /2 Figura 5.6

Exemplo 5.6 segundo:

0

1

2 3 fs /2 f1

4 fs

5

6

7

8

Hz

Replicações para f1 = 3 Hz e fs = 4 amostras/segundo.

O que acontece se f1 > fs? Com k = −1, consideraremos f1 = 5 Hz e fs = 4 amostras/ cos(2 (5 − 4)nT s) = cos(2 (1)nT s) = cos(2 (−1)nT s).

Ou seja, isso também aparecerá como uma frequência negativa. Veja a Figura 5.7. Note que também teríamos uma réplica em −5 Hz.

–8 –7 –6 –5 –4 –3 –2 –1 –fs –fs /2 Figura 5.7

0

1

2 3 fs /2

4 fs

5 f1

6

7

8

Hz

Replicações para f1 = 5 Hz e fs = 4 amostras/segundo.

Vemos que cos(2 f1nT s) parece o mesmo que cos(±2 (f1 − fs)nT s), independentemente de onde f1 estiver em relação a fs.

Os dois gráficos anteriores, Figuras 5.6 e 5.7, são muito semelhantes. O último exemplo não teria também uma réplica em 3 Hz? Vamos repetir o quarto exemplo: cos(2 (5 − 4)nT s) = cos(2 (1)nT s) = cos(2 (−1)nT s), mas cos(2 (5 − 4)nT s) também é igual a cos(2 (5 − 8)nT s) para este T s específico. Se considerarmos k = −2: cos(2 (5 − 8)nT s) = cos(2 (−3)nT s) = cos(2 (3)nT s),

Weeks 005.indd 159

13/04/12 07:00

160

CAPÍTULO 5

–8 –7 –6 –5 –4 –3 –2 –1 –fs –fs /2 Figura 5.8

0

1

2 3 fs /2

4 fs

5 f1

6

7

8

Hz

Replicações para f1 = 3 ou 5 Hz e fs = 4 amostras/segundo.

Portanto, realmente vemos réplicas também em ±3 Hz. Na verdade, temos uma réplica do sinal de 5 Hz em todas as frequências de numeração ímpar nesse espectro. (Veja a Figura 5.8.) Isso pode ser um problema – não podemos distinguir o sinal de 3 Hz no Exemplo 3 do sinal de 5 Hz no Exemplo 4. Se tivéssemos ambos os sinais de 3 Hz e 5 Hz nesses exemplos, um interferiria no outro.

5.5

A Taxa de Nyquist

Nas seções anteriores, vimos que a taxa de amostragem fs deve ser maior do que o componente de maior frequência para evitar a sobreposição espectral. Vimos também como essa frequência maior f deve ser inferior à metade da taxa de amostragem fs, para evitar o dobramento. Na próxima seção, veremos que a largura de banda é o termo importante; até este ponto, partimos do princípio de que estamos interessados em todas as frequências a partir de 0 Hz até o componente de maior frequência. Isso implica que a largura de banda é igual ao componente de maior frequência. Na realidade, é mais preciso afirmar que a frequência de amostragem mínima é baseada na largura de banda. Em outras palavras, fs > − 2(largura de banda). Chamamos essa taxa mínima de amostragem para um sinal de taxa de Nyquist. Outros textos referemse à teoria da amostragem de Shannon, mas trata-se da mesma coisa. Para encontrá-la, tome a largura de banda (geralmente a frequência máxima do sinal) e multiplique-a por um fator 2. A razão pela qual essa taxa é importante é o fato de ela evitar a sobreposição espectral, um problema decorrente da amostragem.

Exemplo 5.7 x(t) = 2 cos(2 700t − 5 /2) + 3 cos(2 450t) + cos(2 630 + 2 /5) Qual a taxa mínima de amostragem para esse sinal? Parta do princípio de que estamos interessados em todas as frequências que começam em 0 Hz. Resposta: Em primeiro lugar, coloque as senoides na forma ak × cos(2 fkt + k). As frequências (fk) em x(t) são: 700, 450 e 630 Hz. Vemos que a maior é 700 Hz. A partir da premissa da pergunta – a de que desejamos todas as frequências que começam em 0 Hz –, nossa largura de banda é, portanto, 700 Hz − 0 Hz = 700 Hz.

Desse modo, nossa taxa de amostragem deve ser no mínimo 1400 Hz. Esse processo é chamado de amostragem passa-baixas, por registrar com precisão todas as frequências inferiores ao componente de maior frequência. Entretanto, para o caso de não precisarmos de todas as frequências entre 0 e a frequência máxima (nesse caso, 700 Hz), a Seção 5.6 mostra outra maneira de executarmos a amostragem.

Weeks 005.indd 160

13/04/12 07:00

Amostragem

5.6

161

Amostragem de Banda Limitada

É possível amostrar um sinal a uma taxa inferior ao dobro de sua frequência mais alta caso o sinal possua uma largura de banda limitada. Isso é chamado de amostragem de banda limitada. Vamos começar com um exemplo. Suponha que exista um sinal composto de quatro senoides, com frequências 1010 Hz, 1020 Hz, 1030 Hz e 1040 Hz. x(t) = 4 cos(2 1010t) + 6 cos(2 1020t) + 8 cos(2 1030t) + 10 cos(2 1040t) A Figura 5.9 mostra o espectro do sinal amostrado. Por opção, o número de amostras simuladas é um múltiplo de fs, o que nesse caso significa que as frequências de análise da DFT coincidem exatamente com as frequências presentes no sinal. Em outras palavras, não há vazamento espectral de DFT (veja a Seção 6.8), de modo que as figuras têm boa aparência. Também nessa figura, somente uma parte do espectro é mostrada. As amplitudes abaixo de 1000 Hz na Figura 5.9 são todas iguais a zero. Uma observação final sobre essa figura é que foi escolhida uma frequência de amostragem simulada de 2081 amostras/segundo pelo fato de ela proporcionar uma figura de boa aparência. Se fossem utilizadas 2080 amostras/segundo, o componente de frequência em 1040 Hz pareceria ter o dobro da amplitude. Seria possível amostrar o sinal x(t) a 2080 Hz ou acima, como mostra a Figura 5.9, mas o que aconteceria quando o amostrássemos a 100 Hz? Teríamos conteúdo espectral, conforme mostrado na Figura 5.10. Como podemos ver a partir desses gráficos, a mesma informação está presente. Existem vantagens na amostragem a uma taxa mais baixa – a saber, a de que o equipamento para tal pode ter um custo mais baixo. Mas por que isso funciona? A resposta encontra-se na mesma análise que utilizamos com a sobreposição espectral. Quando um sinal é amostrado (digitalizado), valores são lidos periodicamente a partir do sinal analógico. Efetivamente, isso significa que substituímos t por n/fs na representação matemática do sinal analógico. Não é provável

Sinal amostrado (após DFT) fs = 2081, N = 20810

1

Amplitude

0.8 0.6 0.4 0.2 0 995

1000

1005

1010

1015 1020 1025 Frequência (Hz)

1030

1035

1040

1045

45

50

Figura 5.9 Sinal de exemplo amostrado a 2081 Hz.

Sinal amostrado (após DFT) fs = 100, N = 1000

1

Amplitude

0.8 0.6 0.4 0.2 0 0

5

0

15

20 25 30 Frequência (Hz)

35

40

Figura 5.10 Sinal de exemplo amostrado a 100 Hz.

Weeks 005.indd 161

13/04/12 07:00

162

CAPÍTULO 5

que tenhamos uma representação matemática de nosso sinal analógico. Afinal de contas, se soubéssemos o que era o sinal analógico, por que nos preocuparíamos em amostrá-lo? Contudo, qualquer sinal analógico pode ser representado como uma soma de senoide, portanto é razoável presumir que qualquer sinal analógico possua uma representação matemática em uma forma como essa. A forma para cada componente senoidal é a × cos(2 ft + ). Quando amostramos o sinal, esses componentes se tornam a × cos(2 fn/fs + ). Sabemos que cos(2 k + ) = cos(), desde que k seja um inteiro. Isso não surpreende; como a função cosseno é periódica, ela se repete indefinidamente – em ambas as direções, positiva e negativa (ou seja, k pode ser um inteiro positivo ou negativo). Assim, um componente de um sinal analógico se repetirá, se houver tempo suficiente. Quando fs = 100 amostras/segundo, o primeiro componente se torna:

Visto que n é um inteiro (ou seja, considere k = 10n), isso reduz-se a: 4 cos(2 n/10). Em outras palavras, o componente de 1010 Hz aparece como um componente de 10 Hz. Deixamos para o leitor a tarefa de verificar se os outros componentes aparecerão como 20 Hz, 30 Hz e 40 Hz quando amostrados a essa taxa. Além disso, encorajamos o leitor a mostrar que isso funciona quando existe um ângulo de fase diferente de zero – por exemplo, para 4 cos(2 1010n/fs +  /4). Esse exemplo funciona porque não existe conflito entre frequências. Mas o que aconteceria se já houvesse um componente de 10 Hz? Nesse caso, a informação seria perdida, pois ambos os componentes, de 10 Hz e 1010 Hz, pareceriam ser um único componente em 10 Hz. Evitar a sobreposição é um aspecto a ser observado durante a amostragem de banda limitada. Como acabamos de ver, a amostragem de banda limitada depende da frequência de amostragem. Se fs fosse, digamos, 103 amostras por segundo, o primeiro componente seria:

Como n é um inteiro, isso se reduz a: 4 cos(2 83n/103). Não vamos parar por aqui. Como 83n/103 é equivalente a (103 − 20)n/103, podemos eliminar outro termo 2 n, o que nos deixa com um componente de frequência que parece ser de –20 Hz. Fazemos isso porque ele aparecerá como −20 Hz de qualquer maneira; o espectro do sinal contém informações de −fs/2 a fs/2, com a mesma informação em ambos os lados, refletidos em torno de 0. Esse componente de frequência 83/103 é maior do que 103/2 (ou seja, fs/2), de modo que ele aparece no espectro como −20/103. Visto que a metade esquerda do espectro para um sinal real é um espelho da metade direita, isso aparece como +20 Hz também. Em outras palavras, cos(−) = cos(), para qualquer ângulo  real. Da mesma forma, quando removemos múltiplos de 103 dos demais componentes, obtemos: x[n] = 4 cos(2 − 20n/103) + 6 cos(2 − 10n/103) + 8 cos(0) + 10 cos(2 10n/103). A informação tanto no segundo quanto no quarto componente aparece em 10 Hz, o que significa que perdemos informação. A Figura 5.11 mostra como o espectro seria se amostrássemos a 103 Hz. Um ponto interessante é que o componente em 0 Hz parece ser duas vezes maior, tal como será qualquer componente em 0 Hz. As amplitudes são relativas entre si, e como esperamos, digamos, que 10 Hz tenha uma amplitude igual em −10 Hz, devemos esperar isso também para 0 Hz e −0 Hz. Ao utilizarmos 2081 amostras/ segundo conforme fizemos na Figura 5.9, evitamos a duplicação da amplitude mostrada em 1040 Hz.

Weeks 005.indd 162

13/04/12 07:00

Amostragem

1

163

Sinal amostrado (após DFT) fs = 103, N = 1030/2.

Amplitude

0.8 0.6 0.4 0.2 0 0

10

20

30 Frequência (Hz)

40

50

60

Figura 5.11 Sinal de exemplo amostrado a 103 Hz.

Toda a informação do sinal real aparecerá no espectro entre 0 e fs/2, mesmo que ele possua um componente de frequência acima de fs/2. Essa é a ideia por trás da taxa de amostragem de Nyquist; fs/2 deve ser no mínimo tão grande quanto a largura de banda. Como provavelmente conhecemos a largura de banda e precisamos resolver fs, isso se torna a relação fs > − 2B. Podemos encarar o mapeamento da informação do sinal para o espectro, de 0 a fs/2, de duas maneiras. A primeira é que essa informação espectral será repetida, indefinidamente, para cada múltiplo de fs. A segunda é imaginar que qualquer informação espectral existente no sinal analógico será mapeada para uma frequência entre 0 e fs/2, da mesma forma como um espelho curvo reflete tudo à frente dele, mesmo que o espelho seja menor do que as coisas refletidas.1 Se formos cuidadosos para evitar a sobreposição (aliasing), poderemos tirar vantagem disso. Como escolher um valor para fs? Conforme indicado anteriormente, um limite equivale ao dobro da largura de banda, ou seja, fs > − 2B. Outro aspecto a ser considerado é que cada componente de frequência deve ser mapeado para uma frequência única entre 0 e fs/2. Isso pode ser obtido com a equação a seguir [12].

A variável fc é a frequência central, definida como o ponto médio entre a frequência de interesse mais baixa e a frequência de interesse mais alta. Outra maneira de visualizar isso é sob a forma da frequência de interesse mais alta menos a metade da largura de banda. A variável m é um inteiro e fornece o número de replicações ou o número de vezes que a informação parece se repetir entre 0 e a frequência de interesse mais baixa. Ele é semelhante ao número de múltiplos de fs que podemos remover de nossas frequências f originais. Vamos retornar ao nosso exemplo anterior, em que estávamos interessados na informação entre 1010 Hz e 1040 Hz. Aqui, B = 1040 − 1010 = 30 Hz e fc = 1010 + B/2 = 1025 Hz. Podemos encontrar faixas para fs computando a equação anterior para valores crescentes de m. Se utilizamos m = 20, obtemos a faixa 2020/20 Hz > − fs > − 2080/21 Hz ou 101 Hz > − fs > − 99.0476 Hz. Constatamos que fs = 100 amostras/segundo cai dentro dessa faixa. Também precisamos nos certificar de que o critério de Nyquist seja satisfeito e, como 100 Hz > − 2B ou 100 Hz > − 60 Hz, constatamos que ele é. Se repetirmos o cálculo utilizando m = 19, obteremos a faixa de 106.3158 Hz > − fs > − 104 Hz. Há dois aspectos a serem observados. O primeiro é que a taxa de amostragem de 103 amostras/segundo está fora dessas faixas, o que explica o porquê de não sermos capazes de utilizá-la no exemplo anterior. O segundo é que, se tivéssemos de utilizar uma taxa de, digamos, 105 amostras/segundo (que se encontra na faixa anterior), obteríamos o gráfico da Figura 5.12. O que é interessante aqui é o fato de obtermos a mesma informação – só que ela é invertida em relação àquela que esperamos. Vamos examinar o primeiro componente do sinal, para fs = 105 amostras/segundo:

1

Weeks 005.indd 163

Para uma representação artística dessa ideia, consulte “Hand with Reflecting Sphere”, de M. C. Escher.

13/04/12 07:00

164

CAPÍTULO 5

Sinal amostrado (após DFT) fs = 105, N = 1050

1

Amplitude

0.8 0.6 0.4 0.2 0 0

10

20

30 Frequência (Hz)

40

50

60

Figura 5.12 Sinal de exemplo amostrado a 105 Hz.

Isso se reduz a: 4 cos(−2 40n/105) e, uma vez que cos(−) = cos(), esse componente de 1010 Hz aparece em 40 Hz. Da mesma forma, os outros componentes de frequência aparecem invertidos em relação ao original. A informação existe, mas é invertida quando m é um número ímpar. Se isso é aceitável ou não, depende da aplicação. A propósito, se tivéssemos que escolher fs = 110 amostras/segundo, que pertence à faixa de fs quando m = 18, teríamos a informação na ordem correta, como mostra a Figura 5.13. Para uma visão geral sobre o desenvolvimento da equação que fornece a faixa de fs, consulte Understanding Digital Signal Processing, de R. Lyons [12]. A Tabela 5.1 resume essa análise. Ela mostra que a utilização de 103 amostras/segundo resulta em duas frequências mapeando para o mesmo valor observado. As demais frequências de amostragem são apropriadas para uso, pois cada frequência real mapeia para uma única frequência observada.

Sinal amostrado (após DFT) fs = 110, N = 1100

1

Amplitude

0.8 0.6 0.4 0.2 0 0

10

20

30 Frequência (Hz)

40

50

60

Figura 5.13 Sinal de exemplo amostrado a 110 Hz.

Tabela 5.1

Weeks 005.indd 164

Frequências detectadas com amostragem de banda limitada. Frequência real Frequência de 1010 1020 1030 1040 amostragem 100 10 20 30 40 103 20 10 0 10 105 40 30 20 10 110 20 30 40 50

13/04/12 07:00

Amostragem

165

RESUMO . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Este capítulo abrange o tópico de amostragem. Amostragem é o processo de leitura de sinais por meio de um computador, também chamado de conversão analógica-digital (ADC). Podemos tomar o caminho inverso e converter de digital em analógico, com um conversor digital-analógico (DAC). Ao lidarmos com a amostragem, contudo, existem armadilhas a serem evitadas, tais como a sobreposição espectral e o dobramento. A sobreposição espectral significa que uma frequência f1, amostrada à taxa de fs amostras/segundo como em cos(2 f1nT s), aparece como cos(2 (f1 + kfs)nT s), para todos os valores k inteiros e positivos. O dobramento significa que a frequência f1, amostrada à taxa de fs amostras/segundo como em cos(2 f1nT s), aparece como cos(2 (f1 − kfs)nT s), novamente para todos os valores em que k é um inteiro positivo. Muitos livros-texto combinam a sobreposição espectral e o dobramento, pois trata-se simplesmente de permitir que k seja negativo. Note que uma frequência negativa entre −fs/2 e 0 aparecerá como uma frequência positiva entre fs/2 e fs. Ou seja, se −fs/2 < f1 < 0, ela terá uma replicação em f1 + fs. Se considerarmos f1 = −fs/3, ela terá uma replicação em fs − fs/3 = 2fs/3, que claramente encontra-se entre fs/2 e fs. Visto que a função cosseno é uma função par, cos(+) = cos(–), portanto um componente de frequência cos(2 f1nT s) também aparece como cos(2 (−f1)nT s), o que não é nenhuma surpresa. A fórmula inversa de Euler cos() = (e j  + e j(−))/2 significa que para cada termo da senoide haverá dois componentes no espectro – em mais e menos a frequência. Cada componente de frequência cos(2 f1nT s) aparecerá no espectro digital como cos(±2 (f1 ± kfs)nT s). O critério de Nyquist deve ser observado, mas a amostragem de banda limitada torna possível tirar vantagem da utilização de replicações por meio da amostragem a uma taxa inferior à esperada.

EXERCÍCIOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1. Se o período de amostragem for de 8 milissegundos, qual será a frequência de amostragem? 2. O que é sobreamostragem? Por que você a utilizaria (ou por que não)? 3. O que é subamostragem? Por que você a utilizaria (ou por que não)? 4. Modifique o exemplo de código de amostragem (da Seção 5.4.1) para demonstrar o dobramento. 5. O que significa “reconstrução”? 6. O que significam “ADC” e “DAC”? 7. Se você conectasse a saída de um ADC à entrada de um DAC, a saída do DAC seria exatamente a mesma que a entrada do ADC? Por que ou por que não? 8. O que x[n] = x(nTs) implica? 9. Mostre que a sobreposição espectral ocorre para mais de uma senoide, tal como quando amostramos x(t) = 3cos(2 300t +  /3) + 8cos(2 800t −  /5) a fs = 500 Hz. 10. Suponha que amostremos o seguinte sinal x(t) a fs = 10000 amostras/segundo. x(t) = 3 cos(2 500t −  /5) + 6 cos(2 700t +  /5) + 4 cos(2 600t + 2 /7) (a) (b) (c) (d)

Quantas amostras teríamos após 16 milissegundos? Qual a equação para x[n]? Escreva o que são x[0] e x[1] e compute o valor para x[0]. Se utilizarmos a amostragem de banda limitada, a taxa de amostragem será apropriada? E se utilizarmos a amostragem passa baixa? Explique.

11. Suponha que tenhamos um sinal com frequências entre 18 kHz e 24 kHz. Se o amostrássemos de tal modo que houvesse duas réplicas desse sinal (entre −18 kHz e +18 kHz), qual(is) seria(m) nossa(s) frequência(s) de amostragem?

Weeks 005.indd 165

13/04/12 07:00

166

CAPÍTULO 5

12. Suponha que você deseje amostrar um sinal com uma faixa de frequência de 15 kHz a 20 kHz. Liste todas as frequências de amostragem que seria possível utilizar (considerando que você emprega a amostragem de banda limitada). Quantas frequências de amostragem possíveis existiriam caso você tivesse de utilizar a amostragem passa-baixas? 13. Suponha que tenhamos um sinal com frequências entre 10 MHz e 15 MHz. Se amostrássemos o sinal, que frequência(s) de amostragem seria possível utilizar? Note quantas réplicas do sinal haveria (ou seja, entre −10 MHz e +10 MHz). 14. O sinal analógico x(t) é: x(t) = 3 cos(2 2000t +  /4) + 2 cos(2 5000t) + cos(2 11000t −  /7). (a) Qual a largura de banda de x(t)? (b) Qual a equação para x[n]? 15. Considere que o sinal x(t) = 3 cos(2 2000t +  /4) + 2 cos(2 5000t) + cos(2 11000t −  /7) seja amostrado a 10 kHz. (a) (b) (c) (d) (e)

O que a equação x[n] se torna? Trace x[n]. Se x(t) for amostrado a 10 kHz, que sobreposições espectrais o sinal de 5 kHz terá? Qual a frequência crítica de Nyquist? Você poderia utilizar uma amostragem de banda limitada com esse sinal? Por que ou por que não?

16. Suponha que tenhamos um sinal de interesse entre 6 kHz e 8 kHz e que planejemos utilizar a amostragem de banda limitada. (a) (b) (c) (d)

Qual é a frequência portadora (central)? Qual é a largura de banda? Se amostrássemos esse sinal a 3500 Hz, isso causaria sobreposição espectral? Que frequências de amostragem podemos utilizar para uma, duas e três replicações espectrais?

17. Suponha que tenhamos os seguintes sinais: x 1(t) = 6 cos(2 7000t +  /2) x 2(t) = 4 cos(2 8000t) x 3(t) = 2 cos(2 6000t). (a) Trace cada sinal. Na mesma página, mostre também o traçado do sinal com o argumento da frequência negativado (ou seja, cos(−2 ft − )). (b) O que você pode concluir sobre esses traçados? (c) Sua conclusão seria a mesma se fosse utilizada a função seno? Por que ou por que não? (Não é preciso mostrar um gráfico, mas explique sua resposta.) 18. Se utilizássemos x(t) como se segue e o amostrássemos a 50 kHz, quantas amostras teríamos após 25 milissegundos? x(t) = 3 cos(2 2000t +  /4) + 2 cos(2 5000t) + cos(2 11000t −  /7) 19. Dados x[n] = x(nT s) = cos(2 1000nT s)/2 + cos(2 2000nT s +  /4) e fs = 8 kHz, utilize o MATLAB para encontrar as primeiras 10 amostras x[n] (n = 0,..., 9). 20. Suponha que você deseje amostrar um sinal com uma faixa de frequência de 14 kHz a 20 kHz. Qual é a frequência de amostragem mais baixa que você pode utilizar?

Weeks 005.indd 166

13/04/12 07:00

Amostragem

167

21. Suponha que você possua um sinal com frequências entre 1 kHz e 22 kHz e que haja ruído entre 100 kHz e 102 kHz. (a) Se você amostrar o sinal à taxa de 44000 amostras/segundo, o ruído interferirá no sinal? (b) Se você amostrar o sinal à taxa de 30000 amostras/segundo, haverá um problema. Qual? 22. Suponha que tenhamos um sinal com frequências entre 24 kHz e 28 kHz. Se amostrássemos o sinal de tal maneira que houvesse duas réplicas do mesmo (entre −24 kHz e +24 kHz), qual seria nossa frequência de amostragem? 23. Se o período de amostragem é de 25 milissegundos, qual é a frequência de amostragem? E se o período de amostragem for de 25 microssegundos?

PROJETO . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Simule a amostragem de um sinal composto pelas seguintes frequências: 200 Hz, 400 Hz, 1400 Hz e 1600 Hz à taxa de 1200 amostras/segundo. Mostre que a sobreposição espectral combina duas frequências em uma. Dica: examine o programa aliasing.m. Um exemplo de solução é fornecido no site da LTC Editora, como project5.m. Teste resolver este problema por conta própria e só então leia o texto a seguir.

Se considerarmos que o sinal de interesse vai de 0 Hz até a frequência mais alta listada (1600 Hz) veremos que a taxa de 1200 amostras/segundo está muito aquém de 2 × 1600. Um pouco mais de análise revela que as replicações a cada fs colocam uma replicação de 200 Hz em 1400 Hz e uma replicação de 400 Hz em 1600 Hz. Não seríamos capazes de distinguir essas replicações a partir das outras frequências. Começamos codificando as frequências, as magnitudes e os ângulos de fase, juntamente com algumas outras variáveis úteis. Utilize as frequências fornecidas. Os ângulos de fase não têm função neste projeto, e atribuímos às magnitudes um valor padrão igual a 1. Também definimos a frequência de amostragem, fs, de acordo com a informação fornecida, definindo em seguida o período de amostragem, Ts, com base nela. freq = [200, 400, 1400, 1600]; % frequencias de exemplo phase = [0, 0, 0, 0]; % fases de exemplo mag = [1, 1, 1, 1]; % magnitudes de exemplo fs = 1200; % frequencia de amostragem Ts = 1/fs; % periodo de amostragem num_points = 200; % O numero de num_samples = 11; % O numero de step = 1/(min(freq)*num_points); t = 0:step:(1/min(freq)); n = 0:num_samples-1;

pontos a utilizar amostras para simular a leitura % obtenha um incremento apropriado % “tempo” % indice de amostra

As variáveis step e num_points prestam-se ao incremento fracionário e à extensão de nossa lista de tempo simulada, t. Também dispomos de uma lista de amostras, n, definida pelo número de amostras que especificamos. Em seguida, criamos uma matriz denominada x, na qual utilizamos nosso índice de tempo t para obter uma visualização da função analógica. O índice de tempo deve ser apropriado o bastante para que a senoide pareça suave. for m=1:length(freq) x(m, :) = mag(m)*cos(2*pi*freq(m)*t + phase(m)); end

Weeks 005.indd 167

13/04/12 07:00

168

CAPÍTULO 5

Compararemos x com uma versão dele que simula a amostragem. A matriz x2 possui a seguinte definição: for m=1:length(freq) x2(m, n+1) = mag(m)*cos(2*pi*freq(m)*n*Ts + phase(m)); end Note quão similares são as definições: a única mudança substancial é a substituição de nosso índice de tempo (t) simulado pelo nosso índice de amostragem (n*Ts) simulado. A razão para as duas cópias do sinal (x e x2) ficará aparente nos traçados. Na primeira figura, traçamos as quatro senoides armazenadas em x. subplot(2,1,1); plot(t, x(1,:), 'b', t, x(3,:), 'g'); subplot(2,1,2); plot(t, x(2,:), 'r', t, x(4,:), 'y'); Observamos um traçado da senoide de 200 Hz por cima do traçado de 1400 Hz e as senoides em 400 e 1600 Hz no gráfico inferior. As quatro senoides são claramente diferentes. Agora examinaremos as senoides obtidas a partir da amostragem simulada. figure(2) subplot(2,1,1); plot(n,x2(1,:),'bx-', n, x2(3,:), 'go-'); subplot(2,1,2); plot(n,x2(2,:),'rx-', n, x2(4,:), 'yo-'); Elas parecem ser um único traçado em cada subgráfico. Cada ponto coincide exatamente com um ponto no outro sinal. Não podemos apontar qualquer diferença entre as senoides em 200 Hz e 1400 Hz, nem entre aquelas em 400 Hz e 1600 Hz. Portanto, a sobreposição espectral parece combinar duas frequências em uma.

Weeks 005.indd 168

13/04/12 07:00

A Transformada de Fourier

6

Muitos campos diferentes, incluindo medicina, ótica, física e engenharia elétrica, utilizam a transformada de Fourier (FT – Fourier transform) como uma ferramenta de análise comum. Na prática, os padrões utilizados pelos grupos de compressão JPEG (Joint Photographic Experts Group) e MPEG (Motion Picture Experts Group) empregam uma forma modificada da transformada de Fourier. Essencialmente, ela permite que olhemos para as informações de frequência no lugar das informações de tempo, que as pessoas consideram mais natural para alguns tipos de dados. Por exemplo, muitos sistemas estereofônicos são equipados com fileiras de pequenas luzes que brilham de acordo com a intensidade das bandas de frequência. Quanto mais intenso for o agudo, por exemplo, mais as luzes ao longo da fileira brilharão, criando uma barra luminosa que sobe e desce de acordo com a música. Esse é o tipo de informação produzida pela transformada de Fourier. Concentraremos nossa atenção na transformada discreta de Fourier (DFT – Discrete Fourier Transform), pelo fato de ela trabalhar com dados discretos. Nossos dados são discretos no tempo, e podemos considerá-los periódicos. Ou seja, se coletássemos outras N amostras, elas seriam apenas uma repetição dos dados de que já dispomos, em termos das frequências presentes. Nesse caso, utilizamos a DFT, que produz informação de frequência discreta que também consideramos periódica. Na Figura 6.1 vemos o gráfico de resposta de magnitude em frequência para o som “ee”. Ele foi obtido por meio da aplicação da transformada discreta de Fourier aos dados de som. A figura mostra a faixa completa na parte superior e uma visão ampliada na parte inferior.

Resposta de magnitude em frequência para o som “ee”

Magnitude

140 120 100 80 60 40 20 0 0

2000

4000

6000 Hz

8000

10000

Magnitude

140 120 100 80 60 40 20 0 0

50

100

150

200

250

300

350

400

Hz Figura 6.1 Uma pessoa vocalizando o som “ee”.

169

Weeks 006.indd 169

27/04/12 20:23

170

CAPÍTULO 6

Magnitude

4000 3000 2000 1000 0 0

0.5

1

1.5

2

Hz

x104

Magnitude

4000 3000 2000 1000 0 0

Figura 6.2

500

1000

1500

2000 2500 Hz

3000

3500

4000

Adagio de Tocata e Fuga em Dó Maior, de J. S. Bach – resposta de magnitude em frequência.

Já a Figura 6.2 mostra a resposta de magnitude em frequência para outro arquivo sonoro: uma curta gravação de Adagio de Tocata e Fuga em Dó Maior, composta por J. S. Bach. A parte superior dessa figura mostra a faixa completa de frequência, de 0 a 22050 Hz, enquanto a parte inferior mostra uma visão ampliada das primeiras 4500 frequências. Um aspecto interessante a ser observado é o espaçamento regular entre os picos de magnitude. Isso acontece muitas vezes com sinais reais, especialmente com música. Por exemplo, imediatamente antes da frequência de 1000 Hz observamos três picos crescendo em magnitude, correspondendo a três frequências diferentes (porém correlatas). Isso recebe o nome de harmônicos e pode ser visto mais claramente na Figura 6.3, onde uma nota sustentada emitida por uma flauta é tocada. Quatro frequências são bastante pronunciadas (650 Hz, 1300 Hz, 1950 Hz e 2600 Hz), enquanto a maioria das demais frequências é igual a 0. Note como todas essas frequências são múltiplos inteiros de 650 Hz. Flauta

100 90 80

Amplitude

70 60 50 40 30 20 10 0 0

0.5

1 1.5 Frequência (Hz)

2 ⫻104

Figura 6.3 Uma nota sustentada emitida por uma flauta.

Weeks 006.indd 170

27/04/12 20:23

A Transformada de Fourier

171

A faixa de frequência exibida no gráfico anterior (0 a 22050 Hz) não foi escolhida arbitrariamente. Os CDs (Compact Disks) armazenam música gravada a uma taxa de 44100 amostras por segundo, permitindo sons na faixa de 0 a 22050 Hz, que é ligeiramente maior do que a frequência máxima que somos capazes de ouvir. Não chega a ser uma surpresa o fato de que quase todo o conteúdo de frequência na música de Bach mostrado na Figura 6.2 esteja abaixo de 4000 Hz. O órgão, instrumento para o qual essa música foi composta, pode produzir uma gama muito ampla de sons. Alguns órgãos podem produzir notas infrassônicas (abaixo de 20 Hz, que a maioria das pessoas não consegue ouvir) e também as que vão muito além de 10 kHz. Mas essas notas altas não são necessárias para uma música agradável. Para pôr isso em perspectiva, considere o fato de que a maioria dos instrumentos (violão, violino, harpa, tambores, trompas etc.) não consegue produzir notas com frequências fundamentais acima de 4000 Hz, embora os instrumentos efetivamente produzam harmônicos que aparecem acima dessa frequência [27]. Um piano possui uma faixa de 27.5 Hz até um pouco acima de 4186 Hz. A transformada de Fourier é uma forma de mapeamento de dados não periódicos contínuos no tempo para dados não periódicos contínuos na frequência no domínio da frequência. Uma variação denominada série de Fourier trabalha com dados periódicos contínuos no tempo, convertendo-os em uma representação de frequência não periódica e discreta. Quando os dados são discretos no tempo e não periódicos, podemos utilizar a transformada de Fourier discreta no tempo (DTFT – discrete time Fourier transform) para obter uma representação de frequência contínua e periódica. Por fim, quando os dados são discretos no tempo (e podemos considerar que sejam periódicos), utilizamos a DFT para obter uma representação de frequência discreta considerada periódica.

6.1

Transformada Rápida de Fourier versus Transformada Discreta de Fourier

A transformada rápida de Fourier (FFT – fast Fourier transform) é um algoritmo inteligente de implementação da DFT. Conforme esperado, ele fornece os mesmos resultados que os da DFT, só que com muito mais rapidez, graças à eficiência do algoritmo. A FFT representou um grande avanço por permitir aos pesquisadores calcular a transformada de Fourier em um período de tempo razoável. A Figura 6.4 demonstra essa diferença. A curva inferior é um traçado de N log2(N), enquanto as demais são de N 2. Por exemplo, um modelo recente do processador Pentium-4 a 2 GHz efetua cerca de 2 bilhões de cálculos por segundo. Um algoritmo (tal como a DFT) que executa operações N 2 levaria cerca de 5 segundos para processar 100000 amostras de dados. Para 100000000 de amostras de dados, seriam necessários aproximadamente dois meses de processamento! Por outro lado, um algoritmo (tal como o FFT) que executasse operações N log2(N) para 100000000 de amostras de dados precisaria de apenas 1.33 segundo. 10000 9000

Número de operações

8000

** ** * ** ** * 6000 * ** ** * * 5000 ** ** * ** 4000 ** ** * *** 3000 *** * * * *** 2000 *** * * * * **** **** 1000 * * * **** ****** 0 ******************* 0 10 20 30 40 50 60 70 80 90 Tamanho de parâmetro (n) 7000

Figura 6.4

Weeks 006.indd 171

* ** * ** ** * * **

100

Comparação de N log2(N) (linha) versus N 2 (asteriscos).

27/04/12 20:23

172

CAPÍTULO 6

O MATLAB fornece tanto a função fft quanto a função ifft. Para uma velocidade ótima, a FFT requer que o tamanho dos dados seja uma potência de 2, embora o software (tal como o comando fft no MATLAB) não requeira isso. Para a maioria das aplicações, zeros podem ser acrescentados aos dados sem afetar negativamente os resultados. Trata-se de uma prática frequente que visa melhorar a aparência dos resultados. Os zeros não acrescentam nenhuma informação, mas alteram as frequências de análise utilizadas pela FFT/DFT. Essa técnica é chamada de zero-padding (inserção de amostras nulas). Vimos no Capítulo 3, “Filtros”, que os filtros executam a convolução. Pode-se utilizar a FFT para computar a convolução de modo eficiente. Outras aplicações incluem a análise do efeito que um filtro exerce em um sinal e a elaboração de filtros FIR (com a FFT inversa). Sempre que um problema requer a DFT, a FFT pode ser utilizada em seu lugar.

6.2

A Transformada Discreta de Fourier

Esta é a transformada discreta de Fourier (DFT):

em que m = 0,..., N − 1. Chamamos isso de forma retangular da DFT, e existem muitas variações dessa transformada. Por exemplo, uma forma comum de expressá-la utiliza o termo e−j2␲nm/N em vez das senoides, o que pode facilitar a análise feita por uma pessoa.

Eis uma função MATLAB que calcula a DFT. Seu propósito é demonstrar como a DFT pode ser computada, mas ela não exibe a eficiência da função fft embutida no MATLAB. Ela retorna uma lista complexa, que armazena duas informações por número complexo, correspondendo a uma coordenada x e y. Normalmente precisaríamos das magnitudes e dos ângulos de fase. Assim como podemos converter coordenadas cartesianas em coordenadas polares, podemos converter a informação de lista complexa em informação de magnitude e fase. function [X] = dft(x) % % Demonstre a DFT % Xsize = length(x); % execute a DFT (o modo mais dificil) for m=0:Xsize-1 mysumm = 0; for n=0:Xsize-1 mysumm = mysumm + x(n+1) * (cos(2*pi*n*m/Xsize) ... - j*sin(2*pi*n*m/Xsize)); end X(m+1) = mysumm; end Tudo que precisa ser retornado é o vetor de números complexos, ou a variável X. As magnitudes e os ângulos de fase podem ser calculados, embora essa informação torne X redundante. Para a conversão, podemos utilizar as funções abs e angle, tais como: Xmag = abs(X); Xphase = angle(X);

Weeks 006.indd 172

27/04/12 20:23

Weeks 006.indd 173

−j 2 0 0 0 0 0

0e 1e 5e−j 2 2e−j 2 7e−j 2 34e j 2

3 4 5 6 7 Σ

0

9e−j 2

2 −j 2 0

0

4e−j 2

1

9.3e−j 2

7e−j 2

2e−j 2 0.023

0.875

0.75

0.625

−j 2 0.5

5e−j 2

1e

0e

0.25

−j 2 0.375

9e−j 2

0

0.125

6e−j 2

0

6e−j 2

0 4e−j 2

1

0

4.5e−j 2

7e−j 2

2e−j 2 0.426

1.75

1.5

1.25

−j 2 1

5e−j 2

1e

0e

0.5

9e−j 2 −j 2 0.75

0.25

0

4e−j 2

6e−j 2

2

2.25

0.24

2.625

12.7e j2

7e−j 2

2e−j 2

1.875

−j 2 1.5

5e−j 2

1e

0e

0.75

−j 2 1.125

9e−j 2

0

0.375

6e−j 2

3

2e−j 2

7e−j 2

2e−j 2 0

3.5

3

2.5

−j 2 2

5e−j 2

1e

0e

0.5

0

−j 2 1.5

9e−j 2

4e−j 2

6e−j 2

4

5

0.24

4.375

3.75

12.7e−j 2

7e−j 2

2e−j 2

3.125

−j 2 2.5

5e−j 2

1e

0e

1.25

0.625

0

−j 2 1.875

9e−j 2

4e−j 2

6e−j 2

Cálculos DFT de exemplo.

4e−j 2

Tabela 6.1

4.5ej 2

7e−j 2

2e−j 2

0.4

5.25

4.5

3.75

−j 2 3

5e−j 2

1e

0e

1.5

0.75

0

−j 2 2.25

9e−j 2

4e−j 2

6e−j 2

6

9.3ej 2

7e−j 2

2e−j 2

5e−j 2

1e−j 2

0e

1.75

0.875

0

0.023

6.125

5.25

4.375

3.5

−j 2 2.625

9e−j 2

4e−j 2

6e−j 2

7

A Transformada de Fourier

173

27/04/12 20:23

174

CAPÍTULO 6

Note também que os comentários são honestos. Essa não é uma forma muito eficiente de se obter a transformada de Fourier! Mas seu propósito é explicar como a DFT pode ser calculada. Para a DFT, note como os argumentos das funções cosseno e seno são iguais, além de serem funções tanto de n quanto de m. Na verdade, essa transformada distribui os dados unidimensionais originais por uma matriz bidimensional. Vamos ilustrar isso com um exemplo. O código a seguir encontra a DFT de um sinal de exemplo (ele utiliza fft, mas dft também funcionaria, desde que o programa acima estivesse presente). >> fft([6, 4, 9, 0, 1, 5, 2, 7]) ans = Columns 1 through 4 34.0000

9.2426 - 1.3431i

-4.0000 - 2.0000i

0.7574 +12.6569i

-4.0000 + 2.0000i

9.2426 + 1.3431i

Columns 5 through 8 2.0000

0.7574 -12.6569i

A Tabela 6.1 exibe uma matriz na qual calculamos a transformada de Fourier do sinal de exemplo. As linhas correspondem a n, e vemos os valores do sinal de exemplo listados em cada uma das colunas (m). Observe como cada linha possui uma amostra de sinal correspondente (dados no domínio do tempo), enquanto a soma de cada coluna resulta na DFT do sinal (dados no domínio da frequência). Se tivéssemos que encontrar a soma de magnitudes ao longo de cada linha (ou seja, sum(abs(Matrix(r,:)))), obteríamos a amostra no domínio do tempo multiplicada pelo número de pontos (N). Cada valor e −j ␪ possui uma magnitude 1 – esses valores são vetores complexos de magnitude unitária. Essa matriz bidimensioal origina-se da expressão para a DFT, e −j ␪, onde ␪ = 2␲nm/N e N = 8 (nosso tamanho de amostra). Os valores de 2␲nm/N para uma única coluna vão de 0 a 2␲m(N − 1)/N. A variável m também vai de 0 a (N − 1). A DFT inversa, que examinaremos em breve, essencialmente multiplica as entradas da tabela por um vetor e + j ␪, cancelando os vetores complexos originais e nos retornando os dados no domínio do tempo. A soma dos valores de exponenciais complexos pode não ser óbvia. Sempre podemos convertê-los primeiro em coordenadas cartesianas (da forma a + jb). A Tabela 6.2 mostra outro modo de encararmos esses dados, com a forma retangular da equação da DFT. Podemos verificar facilmente que a soma de uma coluna equivale aos resultados da DFT fornecidos pelo MATLAB, pois somamos as partes reais e as partes imaginárias separadamente. Cada coluna resulta em um único ponto no domínio da frequência. Ou seja, para cada saída X[m], as frequências utilizadas para encontrar X[m] são 2␲m(0)/N, 2␲m(1)/N, ..., 2␲m(N − 1)/N. O número de frequências utilizadas depende inteiramente de quantos pontos dispomos, o que é determinado por nossa frequência de amostragem. Portanto, as frequências de análise são fornecidas pela seguinte relação:

6.3

Traçado do Espectro

Para obter um traçado espectral, começaremos com um sinal de exemplo. O código a seguir configura um sinal de exemplo, x. n = 0:99; % numero de pontos fs = 200; % frequencia de amostragem Ts = 1/fs; % periodo de amostragem

Weeks 006.indd 174

27/04/12 20:23

0 1 2 3 4 5 6 7 Σ

0 6.0 + 0.0j 4.0 + 0.0j 9.0 + 0.0j 0.0 + 0.0j 1.0 + 0.0j 5.0 + 0.0j 2.0 + 0.0j 7.0 + 0.0j 34.0 + 0.0j 1 6.0 + 0.0j 2.8 − 2.8j 0.0 − 9.0j 0.0 + 0.0j −1.0 − 0.0j −3.5 + 3.5j −0.0 + 2.0j 4.9 + 4.9j 9.2 − 1.3j

Weeks 006.indd 175

3 6.0 + 0.0j −2.8 − 2.8j −0.0 + 9.0j 0.0 + 0.0j −1.0 − 0.0j 3.5 + 3.5j 0.0 − 2.0j −4.9 + 4.9j 0.8 + 12.7j 4 6.0 + 0.0j −4.0 − 0.0j 9.0 + 0.0j 0.0 + 0.0j 1.0 + 0.0j −5.0 − 0.0j 2.0 + 0.0j −7.0 − 0.0j 2.0 − 0.0j

5 6.0 + 0.0j −2.8 + 2.8j 0.0 − 9.0j 0.0 + 0.0j −1.0 − 0.0j 3.5 − 3.5j −0.0 + 2.0j −4.9 − 4.9j 0.8 − 12.7j

Cálculos DFT de exemplo (forma retangular).

2 6.0 + 0.0j 0.0 − 4.0j −9.0 − 0.0j 0.0 + 0.0j 1.0 + 0.0j 0.0 − 5.0j −2.0 − 0.0j −0.0 + 7.0j −4.0 − 2.0j

Tabela 6.2

6 6.0 + 0.0j −0.0 + 4.0j −9.0 − 0.0j 0.0 + 0.0j 1.0 + 0.0j −0.0 + 5.0j −2.0 − 0.0j −0.0 − 7.0j −4.0 + 2.0j

7 6.0 + 0.0j 2.8 + 2.8j −0.0 + 9.0j 0.0 + 0.0j −1.0 − 0.0j −3.5 − 3.5j −0.0 − 2.0j 4.9 − 4.9j 9.2 + 1.3j A Transformada de Fourier

175

27/04/12 20:23

176

CAPÍTULO 6

% x e nosso sinal de exemplo x = cos(2*pi*20*n*Ts + pi/4) + ... 3*cos(2*pi*40*n*Ts - 2*pi/5) + ... 2*cos(2*pi*60*n*Ts + pi/8); Agora precisamos da informação de frequência de x, que podemos obter a partir da transformada de Fourier. Além disso, estabeleceremos o índice m. X = fft(x); m = 0:length(X)-1; Pode ser útil saber a resolução de frequência. O código a seguir a exibe: >> disp(sprintf('Resolucao de frequencia: a cada %5.2f Hz',... fs/length(X))); Resolucao de frequencia: a cada 2.00 Hz >> Para a visualização do espectro, exibiremos tanto a resposta de magnitude em frequência quanto um traçado dos ângulos de fase. % Trace magnitudes subplot(2,1,1); stem(m*fs/length(X),abs(X), 'b'); ylabel('magnitude'); xlabel('frequencia (Hz)'); title('Resposta de magnitude em frequencia'); % Trace angulos de fase subplot(2,1,2); stem(m*fs/length(X),angle(X), 'b'); ylabel('angulo de fase'); xlabel('frequency (Hz)'); title('Tracado de angulo de fase'); Ao executarmos o código anterior, obtemos os gráficos mostrados na Figura 6.5 e percebemos claramente o conteúdo de frequência em 20 Hz, 40 Hz e 60 Hz, com magnitudes 50, 150 e 100, correspondendo às magnitudes relativas 1, 3 e 2 como em nosso sinal original. Notamos, contudo, alguns aspectos importunos nessa figura. Em primeiro lugar, visto que o sinal original x é real, a resposta de magnitude em frequência possui uma imagem espelhada, de modo que só necessitamos de fato da primeira metade dela. Para as fases, o padrão também é refletido em torno do eixo x, e portanto novamente precisamos somente de metade do traçado. O traçado de fase contém muita informação – na verdade, informação em excesso. Na frequência de 80 Hz, por exemplo, vemos que a amplitude aproxima-se de zero, enquanto o ângulo de fase é relativamente grande. (Nossa expectativa é que os ângulos de fase estejam entre −␲ e +␲.) Para resolvermos o primeiro problema, simplesmente traçamos a primeira metade tanto das magnitudes quanto das fases. Podemos definir uma variável chamada half–m que nos forneça metade da faixa de m e utilizá-la no lugar de m. Mas ao fazê-lo, devemos tomar o cuidado de adicionar 1 a ela antes de usá-la como um índice de lista, uma vez que ela começa em 0. A multiplicação por fs/length(x) converte o eixo x de um índice de números de amostra para frequências em Hz. half_m = 0:ceil(length(X)/2); stem(half_m*fs/length(X),abs(X(half_m+1)), 'b'); Para o segundo problema – mostrar as fases correspondentes a amplitudes zero –, precisamos de um pouco mais de informação. Vamos examinar o segundo ponto da DFT.

Weeks 006.indd 176

27/04/12 20:23

A Transformada de Fourier

177

Resposta de magnitude em frequência

Magnitude

150

100

50

0

0

20

40

60

140

160

180

200

140

160

180

200

Traçado de ângulo de fase

4 Ângulo de fase

80 100 120 Frequência (Hz)

2 0 –2 –4

0

20

40

60

80 100 120 Frequência (Hz)

Figura 6.5 Espectro para um sinal de exemplo.

>> X(2) ans = -1.6871e-13 - 2.2465e-15i Esse número complexo não nos diz muita coisa; portanto vamos convertê-lo em seu ângulo e raio. >> angle(X(2)) ans = -3.1283 >> abs(X(2)) ans = 1.6873e-13 A amplitude situa-se dentro de um erro de arredondamento de zero. Os comandos anteriores confirmam nossa suspeita de que os ângulos de fase são calculados e mostrados para cada valor X mesmo quando estão próximos de zero. A correção disso requer o uso de um valor de tolerância. Se a magnitude for menor que a tolerância, deveremos considerar uma fase zero. Afinal de contas, as magnitudes correspondem a quanto uma senoide contribui para o nosso sinal. Se uma senoide contribui com aproximadamente zero, não faz sentido manter um ângulo de fase diferente de zero para ela. As próximas três linhas permitem que ignoremos as fases que correspondem às magnitudes muito pequenas.

Weeks 006.indd 177

27/04/12 20:23

178

CAPÍTULO 6

tolerance = 0.00001; X2 = ceil(abs(X) - tolerance); X3 = round(X2 ./ (X2+1)); % X3 acima é um vetor de 0s e 1s Os comandos anteriores podem ser um pouco confusos, mas essas linhas definem um vetor binário. A operação de subtração separa as magnitudes para nós. Uma vez que todas as magnitudes são positivas, quaisquer valores negativos após a subtração correspondem às fases que devemos ignorar, e a função ceil as tornará iguais a zero. O problema agora é mapear para 1 os valores diferentes de zero em X2. A divisão de cada valor de X2 por si mesmo (mais 1) retornará valores 0 ou quase 1, ao mesmo tempo evitando um erro de divisão por zero. A função round simplesmente se encarrega de tornar os valores “quase 1” iguais a 1. O código resulta no vetor X3, consistindo em zeros para as fases a ser ignoradas e uns para as fases que desejamos ver. Podemos então utilizá-lo no diagrama de ramos e folhas seguinte, para zerar quaisquer fases com magnitudes próximas de zero. O traçado a seguir não utiliza a variável half–m anterior, mas podemos reunir tudo isso. subplot(2,1,2); stem(m*fs/length(X),angle(X).*X3, 'b'); Agora podemos reunir tudo isso em um programa, para traçar o espectro. Ao executarmos esse programa, obtemos os gráficos mostrados na Figura 6.6, com as magnitudes e os ângulos de fase traçados em relação às frequências. Esse programa é mostrado integralmente, pois seus blocos acabaram de ser mostrados. Para resumir o que ele faz, primeiramente ele cria um sinal de exemplo somando diversas senoides. Em seguida, ele encontra a transformada de Fourier e exibe a resolução de frequência. Depois, ele traça as magnitudes. Ele também traça as fases, mas primeiro zera aquelas que possuem uma magnitude correspondente muito pequena. % spectrum.m % % Mostre o espectro de um sinal de exemplo. % % Defina um sinal de exemplo n = 0:99; % numero de pontos fs = 200; % frequencia de amostragem Ts = 1/fs; % periodo de amostragem % x e nosso sinal de exemplo x = cos(2*pi*20*n*Ts + pi/4) + ... 3*cos(2*pi*40*n*Ts - 2*pi/5) + ... 2*cos(2*pi*60*n*Ts + pi/8); % Encontre o espectro X = fft(x); %m = 0:length(X)-1; half_m = 0:ceil(length(X)/2); disp(sprintf('Resolucao de frequencia: a cada %5.2f Hz',... fs/length(X))); % Trace magnitudes subplot(2,1,1); %stem(m*fs/length(X),abs(X), 'b'); stem(half_m*fs/length(X),abs(X(half_m+1)), 'b'); ylabel('magnitude'); xlabel('frequencia (Hz)');

Weeks 006.indd 178

27/04/12 20:23

A Transformada de Fourier

179

Resposta de magnitude em frequência

Magnitude

150

100

50

0

0

10

20

30

40 50 60 Frequência (Hz)

70

180

90

100

140

160

180

200

Traçado de ângulo de fase

1

Ângulo de fase

0.5 0 – 0.5 –1 –1.5 0

20

40

Figura 6.6

60

80 100 120 Frequência (Hz)

Espectro aprimorado para um sinal de exemplo.

title('Resposta de magnitude em frequencia'); % Trace angulos de fase subplot(2,1,2); % As proximas 3 linhas permitem que ignoremos as fases % que correspondem as magnitudes muito pequenas. tolerance = 0.00001; X2 = ceil(abs(X) - tolerance); X3 = round(X2 ./ (X2+1)); % X3 acima e um vetor de 0s e 1s %stem(m*fs/length(X),angle(X).*X3, 'b'); stem(half_m*fs/length(X), ... angle(X(half_m+1)).*X3(half_m+1), 'b'); ylabel('angulo de fase'); xlabel('frequency (Hz)'); title('Tracado de angulo de fase'); Podemos substituir a definição de x por qualquer outro sinal que desejemos e reutilizar esse código para traçar seu espectro.

6.4

Inserção de Amostras Nulas (Zero Padding)

Quando inserimos amostras nulas em um sinal no domínio do tempo, obtemos uma resolução de frequência de aparência mais suave. Por que isso acontece? Na prática, a extensão do sinal amostrado determina a resolução de frequência, e a inserção de amostras nulas não acrescenta nenhuma informação nova. Embora o sinal complementado com zeros resulte em uma representação com maior apelo visual no domínio da frequência, ele não eleva a resolução da transformada.

Weeks 006.indd 179

27/04/12 20:23

180

CAPÍTULO 6

Por que podemos inserir amostras nulas em nosso sinal no domínio do tempo? Parte da explicação tem a ver com a transformada contínua de Fourier. Por conveniência, utilizamos a frequência em radianos, ␻ = 2␲f. A utilização de ␻ permite que utilizemos f(t) como o nome de nossa função, sem confundi-lo com a frequência. Esta é a equação para a transformada contínua de Fourier (CFT).

Suponha que f(t) seja um sinal que possui valor zero fora do intervalo [a, b] e considere a < b < c. Podemos, assim, substituir a equação anterior pela seguinte:

Visto que f(t) = 0 fora do intervalo [a, b] por definição, a segunda integral é igual a 0. Podemos acrescentar 0 ao lado direito da equação,

Portanto, para sinais contínuos, F(␻) é o mesmo quer consideremos ou não f(t) fora de seu suporte. Para um sinal discreto, o número de pontos para o sinal no domínio da frequência equivale ao número de pontos no sinal no domínio do tempo, de modo que o acréscimo de zeros significa que haverá mais pontos no domínio da frequência. As frequências de análise, em consequência, tornam-se mais refinadas por estarem mais próximas.

6.5

Teoria do Deslocamento para a DFT

Existe uma teoria de deslocamento para a DFT, que declara que a amostragem de um sinal proporcionará os mesmos resultados (em termos de resposta de magnitude em frequência), mesmo que as amostras sejam deslocadas – tal como a transferência das primeiras k amostras para o final da sequência de amostras. Trata-se de uma teoria importante, por nos informar que não existe um momento crítico para o início da coleta de amostras. O programa a seguir demonstra essa ideia. Note como o comando plot coloca dois sinais no gráfico ao mesmo tempo, um em azul (b, de blue) e outro em verde (g, de green). Tente outras cores como vermelho (r, de red) e preto (k, de blacK). % Demonstre a teoria do deslocamento para a DFT % % parametros a modificar number_of_samples = 16; shift = 3; % Deve ser inferior a number_of_samples fs = 2000; % frequencia de amostragem % Crie nosso sinal de exemplo Ts = 1/fs; range = 1:number_of_samples; x(range) = 2*cos(2*pi*400*(range-1)*Ts) ... + cos(2*pi*700*(range-1)*Ts); % Torne y uma versao deslocada de x y = [x(shift:number_of_samples), x(1:shift-1)];

Weeks 006.indd 180

27/04/12 20:23

A Transformada de Fourier

181

% Obtenha a fft de cada sinal X = fft(x); Y = fft(y); % Encontre as magnitudes Xmag = abs(X); Ymag = abs(Y); % Encontre os angulos de fase Xphase = angle(X); Yphase = angle(Y); % Mostre os resultados em um grafico subplot(3,1,1), plot(range, x,'bd', range, y,'g*'); mystr = strcat('Versoes original (losango azul) e deslocada', ... '(verde *) do sinal amostrado'); title(mystr); subplot(3,1,2), plot(range, Xmag,'bd', range, Ymag,'k*'); title('Magnitude X = losango, Magnitude Y = *'); subplot(3,1,3), plot(range, Xphase,'bd', range, Yphase,'k*'); title('Fase X = losango, Fase Y = *'); A Figura 6.7 mostra como é a saída para esse programa. Embora os ângulos de fase sejam diferentes, as magnitudes em frequência são as mesmas tanto para o sinal original quanto para o sinal deslocado. Devemos esperar que os ângulos de fase sejam diferentes, pois esses valores determinam como um sinal composto “alinha-se” com as amostras. Caso contrário, não seríamos capazes de obter de volta os sinais originais via transformada inversa. A parte superior da Figura 6.7 mostra que a versão deslocada do sinal equivale a uma versão defasada do original. O gráfico intermediário apresenta as amplitudes para ambos, e cada ponto sobrepõe a

Versões original (losango) e deslocada (*) do sinal amostrado 4 *

2

*

0

*

–2

*

*

*

*

* *

* *

*

*

–4 0

* *

2

4

*

6

8

10

12

14

16

Magnitude X = losango, Magnitude Y = *

20

*

*

15 10 5 *

0

0

*

*

*

2

* 4

*

* 8

6

*

*

* 10

*

*

12

14

* 16

Fase X = losango, Fase Y = *

4 2

*

*

* *

*

0

*

*

*

*

–2

*

0

2

Figura 6.7

4

6

*

*

* *

–4

Weeks 006.indd 181

*

8

10

12

* 14

* 16

Exemplo de saída do programa de deslocamento para a DFT.

27/04/12 20:23

182

CAPÍTULO 6

amplitude do outro sinal. Na parte inferior, vemos que as fases são muito diferentes. Essa figura, portanto, resume a teoria do deslocamento para a DFT. Imagine dois computadores no mesmo local. Ambos medem o mesmo sinal, mas um inicia a leitura das amostras antes do outro. Por uma questão de simplicidade, imagine que esse sinal seja uma senoide. Claramente, o sinal fornece as mesmas medições para cada computador – a única mudança é no momento da partida. Assim, nossa expectativa é que as senoides que compõem o sinal básico serão iguais, embora possam aparecer deslocadas em relação ao eixo do tempo. A teoria do deslocamento para a DFT confirma ser o caso.

6.6

A Transformada Discreta de Fourier Inversa

Esta é a transformada discreta de Fourier inversa (IDFT, inverse discrete Fourier transform):

em que n = 0, ..., N − 1. Note o quanto ela é semelhante à DFT; as principais diferenças resumem-se ao termo 1/N e ao sinal de “+” antes da parte complexa. O sinal de “+” não é uma coincidência: a transformada inversa utiliza o que chamamos de complexo conjugado da transformada direta. Um complexo conjugado, em poucas palavras, ocorre quando a parte complexa possui um sinal negativo. Por exemplo, 4 − j2 e 4 + j2 são complexos conjugados. Um asterisco sobrescrito denota conjugação complexa. Se dissermos que a = 3 + j7 e b = 3 − j7, então também poderemos dizer que a = b* ou b = a*. Uma forma alternativa da IDFT segue abaixo, utilizando a fórmula de Euler para substituir as senoides por exponenciais.

Apresentamos a seguir uma função MATLAB que calcula a IDFT. Tal como o programa DFT anterior, esse programa tenciona demonstrar somente como a IDFT pode ser calculada. O MATLAB possui uma função ifft que fornece os mesmos resultados, mas com maior rapidez. A função a seguir encontra a DFT inversa unidimensional de um sinal de entrada, retornando-a como um vetor de números complexos. function [x] = idft(X) % % Uso: % [x] = idft(X); % % Esta funcao NAO e muito eficiente, % mas demonstra como a IDFT e feita. Xsize = length(X); % Faca a reconstrucao for n=0:Xsize-1 for m=0:Xsize-1 arra(n+1,m+1) = X(m+1) * (cos(2*pi*m*n/Xsize) + ... j*sin(2*pi*m*n/Xsize)); end end for n=0:Xsize-1 mysumm = 0; for m=0:Xsize-1

Weeks 006.indd 182

27/04/12 20:23

A Transformada de Fourier

183

mysumm = mysumm + arra(n+1,m+1); end % Divida a extensao x(n+1) = mysumm / Xsize; end O primeiro par de loops aninhados cria uma matriz denominada arra, que armazena cada valor necessário para a reconstrução, de modo muito semelhante à Tabela 6.2. O segundo par de loops aninhados encontra a soma de cada coluna e divide a extensão. Existem muitas maneiras de tornar essa função mais eficiente. A título de exercício, como você faria isso? A sessão de MATLAB a seguir demonstra as funções DFT e IDFT. Primeiramente, são fornecidos alguns valores de exemplo para o sinal mysignal. Em seguida, a função dft é chamada. Após os valores DFT terem sido armazenados na variável M, a IDFT é computada e armazenada em mysignal–2. Esse sinal deve conter a mesma informação que mysignal, e isso mostrou-se verdadeiro. A função round descarta a parte que está além do ponto decimal, e a função real ignora a parte imaginária (que é zero). >> mysignal = [ 7 4 3 9 0 1 5 2 ]; >> M = dft(mysignal) M = Columns 1 through 3 31.0000

4.1716 - 5.0711i

-1.0000 + 6.0000i

-1.0000 - 0.0000i

9.8284 + 9.0711i

Columns 4 through 6 9.8284 - 9.0711i Columns 7 through 8 -1.0000 - 6.0000i

4.1716 + 5.0711i

>> mysignal_2 = idft(M) mysignal_2 = Columns 1 through 3 7.0000 + 0.0000i

4.0000 + 0.0000i

3.0000 - 0.0000i

0.0000 - 0.0000i

1.0000 + 0.0000i

Columns 4 through 6 9.0000 - 0.0000i Columns 7 through 8 5.0000 + 0.0000i

2.0000 + 0.0000i

>> real(round(mysignal_2)) ans = 7

Weeks 006.indd 183

4

3

9

0

1

5

2

27/04/12 20:23

184

CAPÍTULO 6

>> mysignal mysignal = 7

4

3

9

0

1

5

2

Constatamos que o resultado arredondado é o mesmo que o original, demonstrando que as transformadas direta e inversa trabalham juntas conforme o esperado. Esse exemplo específico funciona bem, mas o que dizer de um sinal genérico? A próxima seção examina essa questão.

6.7

DFT Direta e Inversa

Esta seção mostra que, se tomarmos um sinal qualquer, executarmos nele a DFT e, em seguida, executarmos a IDFT no resultado, terminaremos com os mesmos valores com os quais começamos. Ela utiliza muita notação matemática para mostrar este conceito. O leitor poderá estudá-la linha por linha ou simplesmente revisá-la superficialmente. Começaremos relembrando as fórmulas. Transformada discreta de Fourier (DFT):

em que m = 0, ..., N − 1. Transformada Discreta de Fourier inversa (IDFT):

em que n = 0, ..., N − 1. Vamos começar com um sinal de exemplo, x = {x0, x1, x2, x3}. Podemos encontrar a DFT de x utilizando as fórmulas gerais mostradas e, como sabemos que N = 4, podemos substituí-lo na fórmula.

Podemos então encontrar a transformada inversa...

... e substituir os termos X[m] por seus equivalentes.

Weeks 006.indd 184

27/04/12 20:23

A Transformada de Fourier

185

Combinamos os expoentes:

Multiplicamos os termos:

Simplificamos:

Weeks 006.indd 185

27/04/12 20:23

186

CAPÍTULO 6

Simplificamos ainda mais (ej0 = 1):

Vejamos o que obtemos quando n = 1:

Lembre-se de que esses valores exponenciais, tais como 2␲(−6)/4, correspondem a ângulos, e qualquer ângulo maior que 2␲ pode ter um múltiplo inteiro de 2␲ removido de seu valor. Por exemplo, e j2␲(−6)/4 = e−j12␲/4 = e−j3␲ = e−j(2␲+␲) = e−j␲.

Em seguida, substituiremos os exponenciais por senoides, utilizando a fórmula de Euler.

Agora podemos calcular as senoides (Tabela 6.3) e inserir os valores.

Weeks 006.indd 186

27/04/12 20:23

A Transformada de Fourier

Tabela 6.3

187

Senoides que simplificam as coisas para nós. cos(␲/2) = 0,

sin(␲/2) = 1

cos(␲) = −1,

sin(␲) = 0

cos(2␲) = 1,

sin(2␲) = 0

cos(3␲/2) = 0,

sin(3␲/2) = −1

Simplificamos:

Agrupamos os termos:

Portanto, a transformada inversa nos restitui os dados que tínhamos antes da transformada direta. O termo 1/N aparece na transformada inversa para escalonar as saídas de volta à magnitude original das entradas. Mostramos isso somente para um dos valores originais, mas o leitor interessado poderá verificar que isso funciona para todos os quatro valores originais.

6.8

Vazamento Espectral

A DFT não possui resolução infinita. Uma consequência disso é que às vezes as frequências presentes em um sinal não são claramente definidas na DFT, e a resposta de magnitude em frequência parece estar distribuída por diversas frequências de análise. Chamamos a isso vazamento espectral da DFT, demonstrado no código a seguir. Nele simulamos dois sinais amostrados: x1 e x2. Se você olhar com atenção, verá que as equações que definem esses dois sinais são iguais. O que mudamos são os parâmetros: as frequências de amostragem (fs1 e fs2), que alteram os períodos de amostragem (Ts1 e Ts2), e o número de amostras a serem lidas (fornecido pelas extensões de n1 e n2). fs1 = 1000; Ts1 = 1/fs1; n1 = 0:99;

fs2 = 1013; Ts2 = 1/fs2; n2 = 0:98;

x1 = 3*cos(2*pi*200*n1*Ts1 - 7*pi/8) + 2*cos(2*pi*300*n1*Ts1) ... + cos(2*pi*400*n1*Ts1 + pi/4); x2 = 3*cos(2*pi*200*n2*Ts2 - 7*pi/8) + 2*cos(2*pi*300*n2*Ts2) ... + cos(2*pi*400*n2*Ts2 + pi/4); mag1 = abs(fft(x1)); mag2 = abs(fft(x2));

Weeks 006.indd 187

27/04/12 20:23

188

CAPÍTULO 6

Vazamento espectral da DFT: 3cos(2␲200t – 7␲/8) + 2cos(2␲300t) + cos(2␲400t + ␲/4) 200 150

*

*

100

*

50

* *

*

0********************************************************* ********* ********* ******************* 0 10 20 30 40 50 60 70 80 90 100 Espectro de frequência (m), frequências m*1000/100 Figura 6.8

O conteúdo de frequência aparece em frequências de análise exatas.

Vazamento espectral da DFT: 3cos(2␲200t – 7␲/8) + 2cos(2␲300t) + cos(2␲400t + ␲/4) 150

* *

100

*

*

* *

50

* * * * * * ** * * ** ** * * * * * * *** * * ****** * ****** ******************** ******* *** * ******** 0**************** 0

Figura 6.9

10

20 30 40 50 60 70 80 Espectro de frequência (m), frequências m*1013/99

90

100

O conteúdo de frequência aparece difuso sobre as frequências de análise.

Os resultados são mostrados nos gráficos a seguir. Quando coletamos 100 amostras do sinal x a uma taxa de 1000 amostras por segundo, obtemos o traçado espectral mostrado na Figura 6.8.

Ela mostra o que acontece quando as frequências de análise coincidentemente correspondem às frequências presentes no sinal. A segunda figura, 6.9, representa o mesmo sinal, só que com o número de amostras N e a frequência de amostragem fs alterados para 99 amostras a uma taxa de 1013 amostras por segundo, respectivamente. Uma vez que

a alteração de qualquer um desses parâmetros (ou de ambos) afeta as frequências de análise utilizadas na DFT. Para a segunda figura, as frequências de análise não coincidem com as frequências do sinal, de modo que vemos o conteúdo espectral espalhado por todas as frequências. O que mudou não foi o sinal, mas a nossa visualização das informações de frequência. Um profissional treinado, porém, interpretaria a segunda figura como se ela fosse a primeira. Uma janela constitui uma forma de modificação do sinal de entrada de modo que não haja saltos repentinos (descontinuidades) nele. Mesmo que você não utilize uma função janela, ao amostrar um sinal você impõe uma função de janela retangular sobre ele [12]. Em outras palavras, a entrada é considerada igual a 0 para todos os valores antes de você iniciar a amostragem e para todos os valores após concluí-la.

Weeks 006.indd 188

27/04/12 20:23

A Transformada de Fourier

189

Os saltos repentinos (descontinuidades) aparecem na resposta de frequência como a função sinc.1 O vazamento espectral ocorre quando as frequências de análise não caem sobre as frequências reais presentes. Isso significa que a informação real vaza para outras bandejas de saída DFT – ela aparece como outras frequências. O janelamento reduz os lobos secundários da função sinc da transformada contínua de Fourier (continuous Fourier transform – CFT), o que por sua vez diminui o efeito do vazamento espectral, visto que a DFT constitui uma versão amostrada da CFT [12]. Em outras palavras, se olharmos para a CFT de um componente do sinal (ou seja, uma única senoide), veremos a função sinc. Já se olharmos para a CFT de uma versão janelada do mesmo sinal, veremos uma função sinc com níveis de lobos secundários mais baixos (embora com um lobo principal mais largo). Se amostrarmos a CFT, talvez tenhamos a sorte de amostrá-la exatamente no ponto entre lobos secundários, onde existem muitos zeros e um único pico. Mas, sendo mais realistas, nossas amostras cairão onde existem um ou dois picos e diversos valores diferentes de zero, devido aos lobos secundários. Quanto mais baixos forem esses lobos secundários, melhor nossa DFT indicará as frequências efetivamente presentes.

6.9

Harmônicos e a Transformada de Fourier

Os harmônicos e a transformada de Fourier são intimamente ligados. Os harmônicos referem-se ao uso de senoides correlacionadas por uma frequência fundamental f0, ou seja, à soma de senoides de frequências f0, 2f0, 3f0 etc. O programa a seguir demonstra os harmônicos. Seu objetivo é aproximar uma onda triangular com uma soma de senoides. Ao executá-lo, constatamos que a aproximação melhora progressivamente à medida que mais senoides são acrescentadas. Utilizamos uma frequência fundamental de 1 Hz. Após criar o sinal x como uma onda triangular, o programa encontra a DFT correspondente utilizando o comando fft do MATLAB. Em seguida, ele encontra as magnitudes e fases para todas as senoides correspondentes aos resultados da DFT. Por fim, o programa exibe a soma de senoides, fazendo uma breve pausa entre iterações do loop para mostrar o progresso. Essencialmente, esse procedimento executa a DFT inversa. A Figura 6.10 exibe o progresso com aproximadamente um sexto do caminho percorrido (a linha sólida corresponde ao sinal original, e a linha de traço e ponto corresponde à soma de senoides). A execução integral do programa mostra que a aproximação final aparece diretamente sobreposta ao sinal original.

20 18 16 14 12 10 8 6 4 2 0 0

10

Figura 6.10

1

Weeks 006.indd 189

20

30

40

50

60

70

80

90

Aproximação de uma onda triangular com senoides.

Para uma demonstração deste conceito, examine o programa leaky_sinc.m no site da LTC Editora.

27/04/12 20:23

190

CAPÍTULO 6

% % %

Mostre como a funcao DFT pode representar uma onda triangular

% primeiro, crie uma onda triangular for i=1:20 x(i) = i; end k=20; for i=21:40 k=k-1; x(i) = k; end for i=41:60 x(i) = i-40; end k=20; for i=61:80 k=k-1; x(i) = k; end % OK, agora temos a onda triangular como sinal x % Encontre a DFT correspondente e escalone o resultado. % Em outras palavras, represente o sinal x como uma soma de senoides [y] = fft(x); % Escalone o resultado, de modo que ele tenha o mesmo tamanho que o original y = y / length(x); % Converta em coordenadas polares (magnitudes e fases) mag = abs(y); phi = angle(y); % Agora, reconstrua-a % Isto exibe a aparencia da versao “soma de senoides” t=0:(1/length(mag)):1; f = 1; % Nossa frequencia fundamental e 1, pois tempo t=n*m/N a = 0; % Mostre-a, acrescentando outra senoide a cada vez for k=0:length(mag)-1 a=a+mag(k+1)*(cos(2*pi*f*k*t+phi(k+1)) ... + j*sin(2*pi*f*k*t+phi(k+1))); plot(1:length(x), x, 'r', 1:length(a), real(a), 'b-.') pause(0.1); end A primeira coisa que esse programa faz é criar um sinal com o qual trabalhará: uma onda triangular. Mostramos a seguir como o programa trabalha com uma onda quadrada. Observe o loop for final, incluindo os comandos plot e pause. Poderíamos utilizar opcionalmente a função plotharmonic, mas esse programa mostra a reconstrução do sinal original como um processo passo a passo. Experimente novamente o programa anterior, substituindo a onda triangular acima (linhas 5 a 22 do programa) pelos sinais a seguir. É uma boa ideia digitar clear all entre eles. % Crie uma onda dente de serra for i=1:40 x(i) = i; end

Weeks 006.indd 190

27/04/12 20:23

A Transformada de Fourier

191

for i=41:80 x(i) = i-40; end % Agora temos a onda dente de serra como sinal x O progresso da aproximação (um sexto do percurso) aparece na Figura 6.11. Essa figura também mostra o resultado quando todos os termos são utilizados. Note que a maior diferença entre o original e a aproximação corresponde ao ponto final. A aproximação da senoide antecipa que o padrão irá se repetir.

Soma do primeiro 1/6 das senoides

40 30 20 10 0 0

10

20

30

40

50

60

70

80

90

70

80

90

Aproximação com todas as senoides 50 40 30 20 10 0 0

10

Figura 6.11

20

30

40

50

60

Aproximação de uma onda dente de serra com senoides.

Em seguida, definimos x como uma função de onda quadrada. As flutuações abruptas e repentinas tornam a aproximação ainda mais interessante do que antes. % Crie uma onda quadrada for i=1:20 x(i) = 0; end for i=21:40 x(i) = 1; end for i=41:60 x(i) = 0; end for i=61:80 x(i) = 1; end % Agora temos uma onda quadrada como sinal x A Figura 6.12 mostra a aproximação da onda quadrada anterior para um sexto das senoides, bem como para todas as senoides. Redefinimos em seguida x como uma combinação de uma onda dente de serra e uma onda quadrada.

Weeks 006.indd 191

27/04/12 20:23

192

CAPÍTULO 6

Soma do primeiro 1/6 das senoides

1 0.8 0.6 0.4 0.2 0 0 1.2 1 0.8 0.6 0.4 0.2 0 – 0.2 0

10

20

30

40

50

60

70

80

90

70

80

90

Aproximação com todas as senoides

10

20

30

40

50

60

Figura 6.12 Aproximação de uma onda quadrada com senoides.

% Crie uma combinacao de onda quadrada e dente de serra for i=1:40 x(i) = i; end for i=41:80 x(i) = 40; %i-40; end % repita for i=81:120 x(i) = i-80; end for i=121:160 x(i) = 40; %40; end A Figura 6.13 mostra a aproximação de uma onda combinada quadrada + dente de serra para uso de um sexto dos termos, bem como de todos os termos. As versões DFT dos sinais são boas, mas não são perfeitas. As descontinuidades nos sinais, tais como a subida e a queda abruptas da onda quadrada, são difíceis de representar no domínio da frequência. A função impulso simples demonstra isso. É muito fácil representá-la no domínio do tempo:

Porém, para representar isso no domínio da frequência, precisamos de um número infinito de senoides. Para encarar esse problema de outra forma, considere uma senoide simples. No domínio da frequência é fácil representá-la como um pico de meia amplitude nas frequências positivas e negativas (lembre-se da fórmula inversa de Euler). Para todas as demais frequências, ela é igual a zero. Se desejarmos representar esse mesmo sinal no tempo, teremos amplitude × cos(2␲ft + ␾), o que nos fornece um valor de função para qualquer valor de tempo. O registro desse valor do sinal para cada valor de tempo exigiria a anotação de um número infinito de termos. Isso nos leva a uma observação: o que é bem definido no domínio do tempo é representado de forma deficiente no domínio da frequência. Além disso, o que é fácil de representar no domínio da frequência é

Weeks 006.indd 192

27/04/12 20:23

A Transformada de Fourier

193

Soma do primeiro 1/6 das senoides

40 30 20 10 0 0

20

40

60

80

100

120

140

160

180

140

160

180

Aproximação com todas as senoides

50 40 30 20 10 0 0 Figura 6.13

20

40

60

80

100

120

Aproximação de uma onda dente de serra + quadrada com senoides.

difícil de representar no domínio do tempo. Trata-se, na verdade, do princípio da incerteza de Werner Heisenberg [28].

6.10

Amostragem de Frequência e o Espectro

Como vimos com a DFT, quando amostramos um sinal real a uma taxa de fs amostras/segundo, o traçado de magnitude em frequência de fs/2 a fs se parece com uma imagem espelhada de 0 a fs/2. Um sinal real é constituído de componentes tais como cos(␪), ou pode ser colocado nessa forma. Se ele fosse um sinal complexo, haveria um componente jsin(␪), ou algo que pudesse ser colocado nessa forma (tal como um componente j cos(␪)). Visto que cos(␪) = e j ␪/2 + e −j ␪/2, um sinal real sempre possui um componente de frequência positivo e negativo no traçado do espectro. Dado que cos(␪) = e j ␪/2 + e −j ␪/2 e considerando-se a lei de Euler:

Isso significa que um sinal complexo possui igualmente dois componentes de frequência. Naturalmente, se tivéssemos um sinal tal como cos(␪) + j sin(␪), teríamos:

Se ␪ for igual a ␾, então

Weeks 006.indd 193

27/04/12 20:23

194

CAPÍTULO 6

Por tal razão nos preocupamos somente com a primeira metade da resposta de frequência. Quando ela é como a Figura 6.14, dizemos que se trata de um filtro passa-baixa. Isso significa que quaisquer componentes de baixa frequência (mudança lenta) permanecerão após o sinal ter sido processado pelo sistema. Os componentes de alta frequência serão atenuados, ou filtrados. Quando ela é como a Figura 6.15, dizemos que se trata de um filtro passa-alta. Isso significa que quaisquer componentes de alta frequência (mudança rápida) permanecerão após o sinal ter sido processado pelo sistema.

1

f —s 2

Figura 6.14 Resposta de frequência de um filtro passa-baixa.

1

f —s 2

Figura 6.15 Resposta de frequência de um filtro passa-alta.

A resposta de frequência pode ser encontrada executando-se a DFT na saída de um sistema quando a função impulso é fornecida como a entrada. Isso também é chamado de resposta ao impulso do sistema. Para se obter uma visualização mais suave da resposta de frequência, a função impulso pode ser preenchida com zeros. Quanto mais amostras houver na função de entrada para a DFT, melhor será a resolução da saída.

Exercício:

Encontre e trace a resposta de frequência para w e y. Primeiramente, considere x uma função impulso unitário e encontre w[n] e y[n]. Insira amostras nulas nesses sinais – digamos, até 128 valores – de modo que o resultado pareça suave. Em seguida, encontre a DFT (ou FFT) de w e y e trace as magnitudes do resultado. Os traçados devem ir somente até metade das magnitudes, pois a outra metade será uma imagem espelhada.

6.11

O Funcionamento da DFT

Como a DFT seleciona o conteúdo de frequência de uma entrada? Em outras palavras, se tivermos conteúdo de frequência em um sinal de entrada, como a DFT o mapeará para a saída? Exploraremos essa questão agora.

Weeks 006.indd 194

27/04/12 20:23

A Transformada de Fourier

195

Suponha que dispomos de um sinal de entrada razoavelmente simples, uma senoide:

Uma versão digital do mesmo inclui amostras a cada Ts segundos, como em

Agora se executarmos a DFT em x[n], poderemos analisá-la com muito mais facilidade se a convertermos na forma ej. Sabemos que

portanto

(A razão para o multiplicador 2 no sinal x(t) original deve estar clara agora: sua inclusão aqui faz com que os termos de uma metade desapareçam.) Note que x[n] possui duas partes, uma para f e outra para −f. Se lembrarmos que uma função senoidal aparecerá em um espectro tanto como uma frequência positiva quanto como uma negativa, isso faz sentido. Para simplificar as coisas, concentraremos nossa atenção apenas na primeira (ou seja, consideraremos x[n] = e j2␲fnTs). Deste modo, como chegamos até o espectro a partir disso? A DFT nos fornece a equação a seguir.

Se substituirmos x[n] por e j2␲fnTs, obteremos o seguinte:

Sabemos que as frequências de análise da DFT são fornecidas pela relação

Examinaremos dois casos possíveis utilizando essa informação.

6.11.1

Quando a Frequência de Análise Coincide com a Real

Considere o caso em que nossa frequência, f, por acaso corresponde a mfs/N para um dos valores de m (lembre-se de que m é um índice inteiro). Esse seria o caso para algum m quando f fosse um múltiplo de fs/N. Por exemplo, se f = 20 Hz e lemos N = 100 amostras a uma taxa de fs = 200 amostras/segundo. Quando encontramos a DFT em que m = 10, temos uma frequência de análise de 10 × 200/100 = 20 Hz, que corresponde exatamente ao nosso f. Lembre-se de nossa equação para a DFT de e j2␲fnTs.

Agora substitua f por mfs/N.

Weeks 006.indd 195

27/04/12 20:23

196

CAPÍTULO 6

Após a simplificação, temos:

E, como fs = 1/Ts,

Portanto, X[m] reduz-se a n 1. Com um limite superior N para nossa variável de índice n, X[m] = N. Obtemos um valor diferente de zero para X[m] neste caso. E o que dizer dos outros pontos de análise de frequência? Se encontrarmos X[m + 1] substituindo m + 1 na equação, obteremos o seguinte. Tenha em mente que f = mfs/N, e, como nenhum desses valores para f, fs ou N muda, não devemos interferir em m. Pode ser útil pensar em uma frequência específica f = mfs/N, digamos onde fs = 200, N = 100 e m = 10. Logo, f = 20 Hz. A transformada de Fourier terá aquele conteúdo em m = 10. Quando ela calcular o conteúdo de frequência no ponto m + 1, nossa frequência f não mudará. Novamente, lembre-se de nossa equação para a DFT de e j2␲fnTs.

Agora vamos alterar o índice para encontrar o ponto seguinte da DFT.

Se substituirmos f = mfs/N, pois f corresponde ao ponto de frequência anterior, obteremos o seguinte:

Ao percebermos que fsTs é igual a 1, simplificamos:

Em seguida, simplificamos ainda mais:

Os termos e −j 2␲n/N cancelarão um ao outro. Suponha que N = 4. Nossa soma seria

Se tivéssemos que traçar cada componente no plano complexo, teríamos um fasor que repousa ao longo do eixo x positivo, mais um ao longo do eixo j negativo, mais um ao longo do eixo x negativo e um ao longo do eixo j positivo. Em outras palavras, se forem somados, eles irão se cancelar. Isso também vale para outros valores de N. (A título de exercício, encontre cada e − j 2␲n/N para N = 8, esboce os fasores individuais e encontre a soma.) O experimento a seguir confirma esse caso. Criamos um sinal de exemplo denominado x que simula a amostragem 2cos(2␲20t). Encontramos 100 amostras, coletadas à taxa de 200 amostras por segundo. Em seguida, encontramos a FFT e traçamos os resultados.

Weeks 006.indd 196

27/04/12 20:23

A Transformada de Fourier

197

FFT de 2cos(2␲20t) após amostragem simulada 100 90 80

Magnitude

70 60 50 40 30 20 10 0

0

Figura 6.16

10

20

30 40 Frequência (Hz)

50

60

FFT de 2cos(2␲20t) após amostragem simulada.

N = 100; n = 0:N-1; fs = 200; x = 2*cos(2*pi*20*(n/fs)); % sinal de exemplo X = fft(x); % encontre a FFT m = 0:30; stem(m*fs/N, abs(X(m+1))) A Figura 6.16 mostra o traçado resultante. Vemos um único grande pico em 20 Hz, tal como esperávamos. Ele possui uma magnitude 100, correspondendo ao nosso número de amostras (N). O traçado mostra somente parte do espectro, e portanto não vemos o outro pico em −20 Hz.

6.11.2

Quando a Frequência de Análise Não Coincide com a Real

Considere agora o caso em que f = (m + 0.5)fs/N. A título de exemplo, isso ocorrerá quando lermos N = 100 amostras a uma taxa de fs = 200 amostras/segundo, caso nossa frequência original, f, seja de 21 Hz. Quando m = 10, nossa frequência de análise é igual a 10 × 200/100 = 20 Hz. A frequência de análise seguinte, em m = 11, é igual a 11 × 200/100 = 22 Hz. Visto que as frequências de análise ignoram o conteúdo de frequência real, veremos um conteúdo aparente em diversas frequências de análise. Começaremos novamente com a mésima saída da DFT para e j2␲fnTs.

Se considerarmos f = (m + 0.5)fs/N, obteremos o seguinte:

Em seguida, simplificamos a equação.

Weeks 006.indd 197

27/04/12 20:23

198

CAPÍTULO 6

O resultado depende do valor que estipulamos para N. O código a seguir, por exemplo, calcula isso para N = 8. N = 8; n=0:N-1; mysum = sum(exp(j*2*pi*0.5*n/N)); O cálculo resulta em 1 + 5.0273j, com um valor absoluto igual a 5.1258. Se compararmos isso com o exemplo anterior, onde f é um múltiplo inteiro de fs/N, constataremos que o múltiplo inteiro é mais bem representado por aquele ponto único no domínio da frequência. Nosso exemplo, f = (m + 0.5)fs/N, corresponde ao caso em que a presente frequência encontra-se a meio caminho entre dois pontos no domínio da frequência. Curiosamente, podemos observar um vazamento espectral da DFT experimentando outros valores no lugar de +0.5, tal como +1.5. Isso corresponde ao ponto de frequência anterior antes de m, ou seja, o valor para X[m − 1]. Para descobrir o porquê, comece com

substitua m por m − 1 em ambos os lados e, em seguida, substitua f por (m + 0.5)nTs. Isto é simplificado para

Se alterarmos o código para encontrar mysum = sum(exp(j*2*pi*1.5*n/N)), obteremos 1 + 1.4966j e 1.8 como o valor absoluto. Isso nos mostra que obtemos algum conteúdo de frequência informado nesse ponto, embora ele seja muito menor do que o ponto antes dele.

FFT de 2cos(2␲21t) após amostragem simulada 70 60

Magnitude

50 40 30 20 10 0

0

10

20

30 40 Frequência (Hz)

50

60

Figura 6.17 FFT de 2cos(2␲21t) após amostragem simulada.

Weeks 006.indd 198

27/04/12 20:23

A Transformada de Fourier

199

O código a seguir gera um traçado para esse exemplo. Observe que o sinal simulado possui uma frequência de 21 Hz. Com exceção desse aspecto, o código é idêntico ao do exemplo anterior. N = 100; n = 0:N-1; fs = 200; x = 2*cos(2*pi*21*(n/fs)); % sinal de exemplo X = fft(x); % encontre a FFT m = 0:30; stem(m*fs/N, abs(X(m+1))) Vemos o traçado resultante na Figura 6.17. Grandes picos podem ser observados em 20 Hz e em 22 Hz, e podemos imaginar a frequência real de 21 Hz exatamente entre eles. Tal como antes, esse traçado mostra um subconjunto do espectro, de modo que os outros picos em torno de − 21 Hz não estão visíveis. A discrepância entre as frequências de análise e o conteúdo de frequência real resulta no vazamento espectral.

6.11.3

Um Detalhe Adicional

Você pode ter observado algo incomum na Figura 6.17. Em nossa análise, esperávamos que o conteúdo da frequência de 21 Hz aparecesse igualmente em 20 Hz e em 22 Hz, pelo fato de situar-se exatamente entre as duas. No entanto, a figura mostra que o pico em 22 Hz é um pouco mais alto do que em 20 Hz. Por quê? Vamos retornar à nossa análise. Prevalece o fato de que a leitura de 100 amostras à taxa de 200 amostras/segundo nos proporcionará frequências de análise em incrementos de 2 Hz. Em m = 10 (ou 20 Hz), o montante do conteúdo de frequência medido ainda é igual a

enquanto o montante em m + 1 = 11 (ou 22 Hz) será igual a

(Embora não seja mostrado aqui, encontramos X[m + 1] da mesma maneira que encontramos x[m − 1].) Calculamos os valores nesses dois pontos de frequência da seguinte maneira: >> abs(sum(exp(j*2*pi*0.5*n/N))) ans = 63.6646 >> abs(sum(exp(j*2*pi*-0.5*n/N))) ans = 63.6646 Os valores coincidem perfeitamente, aprofundando o mistério. Um traçado dos resultados esperados versus os resultados da FFT ajudaria a esclarecer essa situação. O código a seguir encontra os resultados estimados (est) que esperamos a partir da FFT do fasor e j 2␲21n/fs. Isso é semelhante aos cálculos anteriores, mas para todos os valores de m.

Weeks 006.indd 199

27/04/12 20:23

200

CAPÍTULO 6

f = 21; N = 100; fs = 200; n = 0:N-1; % inicialize nossos resultados est = zeros(1, N); % para cada m, encontre o resultado esperado for m=0:N-1 est(m+1) = sum(exp(-j*2*pi*n*(f/fs - m/N))); end Agora precisamos compará-lo ao sinal de exemplo em 21 Hz. Encontramos o sinal de exemplo e sua FFT, tal como antes. x = 2*cos(2*pi*21*(n/fs)); % sinal de exemplo X = fft(x); % encontre a FFT Agora podemos traçar os dois juntos, mostrando até 60 Hz. m = 0:30; plot(m*fs/N, abs(X(m+1)),'b*', ... m*fs/N, abs(est(m+1)), 'rd') Vemos o traçado como na Figura 6.18. Note que nenhum dos pontos é exatamente o mesmo. Primeiramente os losangos aparecem um pouco mais altos que os asteriscos, e depois os asteriscos aparecem um pouco mais altos que os losangos. Observe também como os losangos são simétricos, espelhando um ao outro em torno de 21 Hz. O vazamento espectral aproxima-se de zero para as frequências subsequentes após a de 21 Hz, mas ainda está presente. Até aqui comparamos os resultados esperados com aqueles obtidos a partir da FFT. Existe uma diferença crucial entre os dois: a FFT utiliza uma função cos, enquanto os resultados esperados advêm somente de um dos dois fasores giratórios equivalentes àquela função. Os resultados para as frequências positivas não deveriam ser os mesmos, uma vez que o outro fasor giratório que compõe a função cos possui uma frequência negativa? Resultados da FFT (*) * versus resultados previstos (losangos) *

60

Magnitude

50 40 30 * 20

* *

** 0* * * * * 0 10

Figura 6.18

Weeks 006.indd 200

*

*

10 *

20

** *** *** ***** ***

30 40 Frequência (Hz)

50

60

Resultados da FFT (asteriscos) versus resultados previstos (losangos).

27/04/12 20:23

A Transformada de Fourier

201

FFT de ej2␲21 (n/fs) (sólida) versus FFT de e–j2␲21 (n/fs) (tracejada) 70 60

Magnitude

50 40 30 20 10 0

0

Figura 6.19

20

40

60

80 100 120 140 Frequência (Hz)

160

180

200

Resultados da FFT de exp(j2␲21(n/fs)) (sólida) versus a FFT de exp(−j2␲21(n/fs)) (tracejada).

Uma das propriedades da FFT é a linearidade. Isso significa que, se executarmos a FFT em ambos os fasores separadamente, os resultados serão iguais aos que obteríamos se executássemos a FFT nos dois somados. Em vez de utilizarmos X, encontraremos X1 e X2, as FFTs de cada fasor. X1 = fft(exp(j*2*pi*f*(n/fs))); X2 = fft(exp(-j*2*pi*f*(n/fs))); Traçamos essas magnitudes resultantes no mesmo gráfico, na Figura 6.19. Agora a razão para a diferença deve se tornar aparente. Com a linha sólida passando pelos pequenos pontos, vemos o conteúdo de frequência esperado para o fasor de 21 Hz. Tal como uma imagem espelhada, a linha tracejada mostra o conteúdo de frequência do fasor em −21 Hz. Mas cada um possui uma longa extremidade que se aproxima de zero e depois começa a subir novamente. Isso explica por que os valores para a FFT da função cos são diferentes. A impressão é a de que o segundo fasor foi o responsável pela diferença no resultado real e, na verdade, o vazamento espectral resultante de sua presença causou isso. Ao examinarmos a Figura 6.19, constatamos que a soma desses dois traçados nos fornecerá um gráfico como o da Figura 6.17. Ao examinarmos um fasor de cada vez, temos de considerar como o vazamento espectral oriundo da DFT de um fasor causará impacto em seu complexo conjugado. Com isso em mente, concluímos que os dois métodos de cálculo do conteúdo de frequência fornecem respostas consistentes. A partir desses exemplos, o leitor deve ser capaz de perceber como o conteúdo de frequência mapeia para a saída da DFT e como as saídas da DFT em outros pontos cancelam umas as outras. Também examinamos como o vazamento espectral aparece quando as frequências de análise não correspondem ao conteúdo de frequência.

6.12

Exibição da DFT como Ângulos em Gráficos de Vetores Unitários

Com a equação inversa de Euler, vemos que uma senoide real no domínio do tempo é vista não como um, mas dois números complexos no domínio da frequência. Os raios são os mesmos, mas os ângulos são opostos.

Weeks 006.indd 201

27/04/12 20:23

202

CAPÍTULO 6

Figura 6.20

Vetores unitários com ângulos diferentes utilizados na DFT.

A Figura 6.20 mostra os vetores unitários utilizados na DFT. Encare-os como uma matriz, multiplicando os dados originais (uma lista xn) para gerar as amostras no domínio da frequência (Xm). Cada vetor pode ser representado por um número complexo da forma c + jd, ou e j2␲␾. Na matriz a seguir, utilizamos bm,n para representar esses números complexos.

Ou podemos mostrar isso como os próprios valores complexos.

Na seção a seguir mostraremos como computar a DFT com uma multiplicação matricial.

Weeks 006.indd 202

27/04/12 20:23

A Transformada de Fourier

6.13

203

Cálculo da DFT Direta e Inversa sob a Forma de Operações Matriciais

Os ângulos para os vetores unitários formam uma matriz, com base em seus respectivos valores de índice n e m – nesse caso, cada um variando de 0 a 7. O segmento de código a seguir mostra esses valores (n × m) designados para uma matriz. a = [0, 0, 0, 0, 0, 0, 0, 0,

0, 0, 0, 0, 1, 2, 3, 4, 2, 4, 6, 8, 3, 6, 9, 12, 4, 8, 12, 16, 5, 10, 15, 20, 6, 12, 18, 24, 7, 14, 21, 28,

0, 5, 10, 15, 20, 25, 30, 35,

0, 6, 12, 18, 24, 30, 36, 42,

0; 7; 14; 21; 28; 35; 42; 49];

Agora podemos utilizar um software para computar os resultados cos e sin para cada um desses valores, ou seja, podemos calcular cos(2*pi*a/8). >> cos(2*pi*a/8) ans = 1 1 1 1 1 1 1 1

1 0.70710 0 -0.70710 -1 -0.70710 0 0.70710

1 0 -1 0 1 0 -1 0

1 -0.70710 0 0.70710 -1 0.70710 0 -0.70710

1 -1 1 -1 1 -1 1 -1

1 -0.70710 0 0.70710 -1 0.70710 0 -0.70710

1 0 -1 0 1 0 -1 0

1 0.70710 0 -0.70710 -1 -0.70710 0 0.70710

Nesse meio tempo podemos calcular os resultados para sin também. >> sin(2*pi*a/8) ans = 0 0 0 0 0 0 0 0

0 0.70711 1 0.70711 0 -0.70711 -1 -0.70711

0 1 0 -1 0 1 0 -1

0 0.70711 -1 0.70711 0 -0.70711 1 -0.70711

0 0 0 0 0 0 0 0

0 -0.70711 1 -0.70711 0 0.70711 -1 0.70711

0 -1 0 1 0 -1 0 1

0 -0.70711 -1 -0.70711 0 0.70711 1 0.70711

O cálculo da DFT significa que multiplicamos a lista de entrada pelos dois conjuntos de matrizes, onde X = (cos(2␲a/8) − jsin(2␲a/8)) × x. (Interprete isso no contexto de programação; a seria uma matriz 8 × 8 e o cosseno dela também seria uma matriz 8 × 8.) A lista x teria oito elementos nesse caso, e essa operação geraria oito elementos complexos X. Os valores de matriz bm,n da seção anterior seriam constituídos a partir dos valores cos(2␲a/8) − jsin(2␲a/8), ou seja, bm,n = cos(2␲am,n/8) − jsin(2␲am,n/8) para todos os valores m e n entre 0 e 7. Para desfazer a transformada posteriormente, encontramos x⬘ = (1/N)(cos(2␲a/8) + jsin(2␲a/8)) × X, em que x⬘ representa o sinal reconstruído. Devemos utilizar a transposição da matriz a, mas podemos simplesmente reutilizar a uma vez que ela é idêntica à sua transposição. Não podemos reutilizar os valores da matriz de bm,n, pois precisamos de seus complexos conjugados.

Weeks 006.indd 203

27/04/12 20:23

204

CAPÍTULO 6

6.14

Um Programa para o Cálculo da DFT sob a Forma de Operações Matriciais

O programa a seguir, denominado doNundoDFT.m, ilustra esse conceito. Primeiramente criamos algum sinal aleatório x contendo oito valores. Definimos N como oito, embora esse código simplesmente espere que haja oito valores. Depois, criamos a matriz a conforme fizemos anteriormente. Em seguida, calculamos a DFT e armazenamos o resultado em X. Por fim, encontramos a DFT inversa. x = round(rand(1,8)*1000) N = 8; a = [0, 0, 0, 0, 0, 0, 0, 0,

0, 0, 0, 0, 1, 2, 3, 4, 2, 4, 6, 8, 3, 6, 9, 12, 4, 8, 12, 16, 5, 10, 15, 20, 6, 12, 18, 24, 7, 14, 21, 28,

0, 5, 10, 15, 20, 25, 30, 35,

0, 6, 12, 18, 24, 30, 36, 42,

0; 7; 14; 21; 28; 35; 42; 49];

% Calcule a DFT X = (cos(2*pi*a/8) - j * sin(2*pi*a/8)) * x.'; % Calcule a IDFT xprime = (1/N)*(cos(2*pi*a/8) + j * sin(2*pi*a/8)) * X Quando comparamos o sinal x original com os valores xprime da DFT inversa, constatamos que eles são iguais. A saída aparece abaixo. >> doNundoDFT x = 822

780

629

407

738

245

923

228

xprime = 822.00 780.00 629.00 407.00 738.00 245.00 923.00 228.00

+ + + -

0.00i 0.00i 0.00i 0.00i 0.00i 0.00i 0.00i 0.00i

Esse programa simples não converte os valores complexos de volta aos valores reais, mas vemos que todas as partes imaginárias são iguais a zero. Em seguida, vamos comparar a DFT que calculamos (variável X) com a função FFT embutida, ou seja, fft(x). Esta é a DFT que calculamos: 4772.0000 335.7300 8.0000 -167.7300 1452.0000 -167.7300 8.0000 335.7300

Weeks 006.indd 204

+ + + +

0.0000i 210.8742i 390.0000i 798.8742i 0.0000i 798.8742i 390.0000i 210.8742i

27/04/12 20:23

A Transformada de Fourier

205

Esta é a DFT calculada pelo programa FFT embutido: >> disp(fft(x).') 4772.0000 335.7300 8.0000 -167.7300 1452.0000 -167.7300 8.0000 335.7300

+ + + + +

0.0000i 210.8742i 390.0000i 798.8742i 0.0000i 798.8742i 390.0000i 210.8742i

Como podemos verificar facilmente, a função FFT embutida retorna os mesmos valores que os de nosso código de multiplicação matricial. Eis um modo alternativo de computar a DFT e a IDFT através de matrizes: criamos a matriz a dinamicamente, o que significa que ela funcionará se mudarmos o valor de N. N = 8; a = (0:N-1).' * (0:N-1); Em seguida, geramos a matriz utilizada para encontrar a DFT. U = (cos(2*pi*a/N) - j * sin(2*pi*a/N)); A matriz U funcionará para o cálculo da DFT de qualquer lista de N valores. Do mesmo modo, criamos uma matriz para encontrar a IDFT. V = (cos(2*pi*a/N) + j * sin(2*pi*a/N)); Novamente, poderíamos utilizar V para encontrar a DFT inversa de qualquer sinal no domínio da frequência com extensão N. Agora, encontramos a DFT para nosso sinal de exemplo, x. X = U * x.'; E encontramos a IDFT com uma operação matricial semelhante. Devemos dividir a extensão da lista em algum lugar ao longo do processo, e o fazemos na etapa final. xhat = (V * X)/N; Para encontrar a DFT, executamos uma operação matricial, multiplicando uma matriz pela lista de dados no domínio do tempo. Encontrar a transformada inversa também é fácil, bastando multiplicar a matriz V pelos dados no domínio da frequência. Assim, qual efeito a transformada e a inversa exercem de um modo geral? A multiplicação da matriz IDFT (V) pela matriz DFT (U) gera a matriz de identidade, vezes N. O código a seguir mostra isso. >> disp(round(V*U)); 8 0 0 0 8 0 0 0 8 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

Weeks 006.indd 205

0 0 0 8 0 0 0 0

0 0 0 0 8 0 0 0

0 0 0 0 0 8 0 0

0 0 0 0 0 0 8 0

0 0 0 0 0 0 0 8

27/04/12 20:23

206

CAPÍTULO 6

Isso mostra por que temos que dividir o número de valores no final. Tenha em mente que a multiplicação por matrizes pode não ser um modo prático de encontrar a DFT e a IDFT. Entretanto, isso deve ajudar o leitor a entender como a transformada funciona e a elucidar as propriedades da mesma.

RESUMO . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Este capítulo abrange a transformada de Fourier, a transformada inversa de Fourier e tópicos correlatos. A transformada de Fourier fornece o conteúdo de frequência (espectral) de um sinal. Dada qualquer série de amostras, podemos criar uma soma de senoides que o aproximam, quando não o representam com exatidão.

EXERCÍCIOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1. Suponha que precisássemos amostrar um sinal à taxa fs = 5000 amostras por segundo e que tivéssemos de coletar 250 amostras. Após executar a DFT, descobrimos que os 10 primeiros resultados são aqueles a seguir. O que podemos concluir sobre as frequências presentes em nosso sinal de entrada? (Considere que os demais valores para X[m] até m = 125 são iguais a 0.)

2. Uma sequência de entrada, x[n], contém a transformada de Fourier, executada nela. O resultado é: X[m] = {3, 2 + j4, 1, 5 − j3, 0, 0, 0, 5 + j3, 1, 2 − j4}. (a) Encontre (e trace) as magnitudes e os ângulos de fase. (b) Você deve notar alguma simetria em sua resposta para a primeira parte. Que tipo de simetria você espera para as magnitudes e por quê? 3. Uma sequência de entrada, x[n], contém a transformada de Fourier, executada nela. O resultado é: X[m] = {3, 2 + j4, 1, 5 − j3, 0, 0, 0, 5 + j3, 1, 2 − j4}. Dado que x[n] foi amostrado a uma taxa fs = 100 amostras/segundo: (a) Qual o componente DC para esse sinal? (b) Quais frequências estão presentes na entrada? Ordene-as de acordo com a amplitude. (c) Utilize MATLAB para encontrar x[n]. 4. Dado x2 = [0.4786, –1.0821, −0.5214, −0.5821, −0.2286, 1.3321, 0.7714, 0.8321], encontre (utilizando MATLAB) os valores Xmagnitude[m] e Xfase[m] da FFT para m = 0, ..., 7. Mostre todo o seu trabalho e represente graficamente seus resultados. 5. Experimente um filtro FIR de 5 taps utilizando os dados aleatórios a seguir. Note que este comando sempre retornará valores diferentes. X = round(rand(1, 20)*100) Utilize h1[k] = {0.5, 0.5, 0.5, 0.5, 0.5} e compare-o com h2[k] = {0.1, 0.3, 0.5, 0.3, 0.1} e h3[k] = {0.9, 0.7, 0.5, 0.7, 0.9}. Represente graficamente a saída do filtro e sua resposta de magnitude em frequência. Certifique-se de utilizar os mesmos valores x. Crie um gráfico com as saídas dos filtros e outro gráfico com as respostas de frequência. Se julgarmos cada conjunto de coeficientes de filtro como um filtro passa-baixa, qual o melhor? Qual o pior? Por quê? 6. Experimente um filtro FIR de 8 taps utilizando os dados aleatórios a seguir. Note que este comando sempre retornará valores diferentes. X = round(rand(1, 20)*100)

Weeks 006.indd 206

27/04/12 20:23

A Transformada de Fourier

207

Utilize h1[k] = {0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5} e compare-o com h2[k] = {0.1, 0.2, 0.3, 0.5, 0.5, 0.3, 0.2, 0.1} e h3[k] = {0.9, 0.7, 0.6, 0.5, 0.5, 0.6, 0.7, 0.1}. Represente graficamente a saída do filtro e sua resposta de magnitude em frequência. Certifique-se de utilizar os mesmos valores x. Crie um gráfico com as saídas dos filtros e outro gráfico com as respostas de frequência. Se julgarmos cada conjunto de coeficientes de filtro como um filtro passa-baixa, qual será o melhor? Qual será o pior? Por quê? 7. O que é 3e − j2␲0.2 em coordenadas cartesianas complexas? (Isto é, na forma a + jb.) Dica: utilize a fórmula de Euler. 8. O que é 1.7 − j3.2 em coordenadas polares complexas? (Isto é, na forma re j ␾.) Dica: utilize os cálculos de magnitude e ângulo do Capítulo 1, “Introdução”. 9. Suponha que você tenha um sinal com conteúdo de frequência em 27.5 Hz e múltiplos inteiros do mesmo. Se a frequência de amostragem fosse de 8192 amostras/segundo, quantas amostras você leria para minimizar o vazamento espectral? 10. Utilize o MATLAB para criar um sinal de exemplo como uma lista de no mínimo 10 valores. Encontre a transformada de Fourier correspondente e trace as amplitudes versus o número da amostra. Encontre também a transformada inversa de Fourier correspondente e compute a diferença entre o original e a reconstrução. O quanto a reconstrução se aproxima do original? Era isso que você esperava?

PROJETO . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Dado um sinal, encontre uma aproximação para ele utilizando o menor número possível de senoides, mas com um erro inferior a 10%. Para este projeto, defina erro como a diferença entre o original e a aproximação, dividida pela energia do sinal original (uma soma de uma elevação ao quadrado ponto a ponto). Dica: examine o programa saw_square_wave.m. Uma solução de exemplo é fornecida no site da LTC Editora, como project6.m. Tente resolver este problema por conta própria antes de ler o texto a seguir.

Precisamos de um sinal com o qual trabalhar, de modo que definimos x como uma onda dente de serra quadrada. Poderíamos utilizar qualquer sinal que desejássemos, embora um sinal regular como esse seja mais fácil de aproximar. Uma vez que o sinal esteja definido, encontramos a FFT correspondente. O plano é reconstruir o sinal a partir das senoides da FFT, essencialmente encontrando a transformada inversa de Fourier. Porém, executamos a reconstrução onda por onda, somando a senoide mais significativa a cada vez. Desta forma, nossa aproximação vai melhorando até atender nosso critério: ter uma diferença de energia inferior a 10%. [X] X = mag phi

= X = =

fft(x); / length(x); abs(X); angle(X);

% % % %

execute a transformada de Fourier escalone o resultado encontre as magnitudes encontre as fases

Precisaremos definir algumas variáveis e listas. Primeiramente, criamos nossa lista de tempo simulado, t, com base no número de amostras. A variável f representa a frequência fundamental, configurada como 1. Consideramos uma resolução de 1 Hz entre pontos de frequência, por não dispormos de nenhuma informação de temporização acerca do sinal que criamos. O índice de tempo, t, dura um segundo simulado. Nossa aproximação inicial, a, é zero. Em breve ela será atualizada para uma lista. Em seguida, estipulamos nosso critério, ten_pct, como 10% da energia do sinal original, conforme especificado pelo projeto. Encontramos um erro inicial, a diferença entre o sinal original e nossa aproximação. Por fim, inicializamos nossa variável de contagem que monitora o número de senoides que acrescentamos à aproximação.

Weeks 006.indd 207

27/04/12 20:23

208

CAPÍTULO 6

step = (1/length(mag)); t = 0:step:1-step; f = 1; % Nossa frequencia fundamental a = 0; % aproximacao ten_pct = 0.10*sum(abs(x)); % meta err = sum(abs(x - a)); % status corrente count = 0; O estágio seguinte mostra a reconstrução passo a passo. Isso ocorre dentro de um loop, de modo que o programa para quando o erro atinge nosso critério ou quando esgotamos nossa lista de senoides a acrescentar. while ((err > ten_pct) && (count < length(mag))) % Encontre a proxima senoide de maior magnitude a acrescentar [v, p] = max(mag); % Acrescente esta senoide a=a+mag(p)*(cos(2*pi*f*(p-1)*t+phi(p)) ... + j*sin(2*pi*f*(p-1)*t+phi(p))); % Marque esta magnitude como usada mag(p) = 0; % Atualize as variaveis do loop err = sum(abs(x - a)); count = count + 1; end Existem três partes principais para o loop: o acréscimo da senoide mais significativa, a remoção da mesma de nossa lista de senoides e a atualização das variáveis que controlam nosso loop. Se examinarmos as magnitudes, encontraremos a maior. O comando [v, p] = max(mag) retorna não apenas o maior valor (v), mas também a posição em que ele aparece (p). Acrescentamos a senoide a nossa aproximação e em seguida sobrescrevemos a informação de magnitude com zero, de forma a não selecioná-la novamente. Por fim, recalculamos nosso grau de erro (err) e incrementamos nossa contagem. O loop também contém um comando plot, para exibir o progresso. Podemos verificar o grau de semelhança entre a aproximação e o original. Para um exercício interessante, experimente valores diferentes para o critério, tais como 5% e 2%. O número de termos utilizados variará consideravelmente e não crescerá linearmente.

Weeks 006.indd 208

27/04/12 20:23

A Transformada z

7

Este capítulo tratará de questões referentes à transformada z, uma ferramenta analítica para sistemas. O que é z? Como essa transformada funciona? Como pode ser utilizada para combinar filtros? Por que as unidades de retardo às vezes possuem um símbolo z−1? Como essa transformada se relaciona com as outras transformadas que já vimos? Responderemos a essas perguntas e outras mais nas seções a seguir. A transformada z é uma versão generalizada da transformada de Fourier. Tal como a transformada de Fourier, ela permite que representemos um sinal no domínio do tempo em termos de seus componentes de frequência. Em vez de acessarmos valores do sinal em termos de n, um índice discreto relativo ao tempo, podemos conhecer a resposta do sinal para uma dada frequência (conforme fizemos com a transformada de Fourier). Tal como a transformada de Fourier, empregamos a convenção de letras maiúsculas para a transformada z. A transformada z serve a dois propósitos. Em primeiro lugar, ela proporciona uma forma conveniente de notação dos efeitos dos filtros. Até aqui utilizamos os coeficientes fornecidos pela função impulso para um filtro FIR, h[n] = {a, b, c, d}, para descrever como a saída y[n] relaciona-se com a entrada x[n]. Na notação da transformada z, indicamos Y(z) = H(z)X(z), onde H(z) é a transformada z de h[n]. Podemos encarar H(z) como algo que opera em X(z) para produzir a saída Y(z). Por essa razão, H(z) também é chamada de função de transferência. Em vez de utilizarmos ax[n − k] na equação, podemos colocar o coeficiente e o retardo (k) juntos e remover x. Portanto, o filtro com coeficientes h[n] = {a, b, c, d} pode ser descrito pela transformada z de h[n], H(z) = az0 + bz−1 + cz−2 + dz−3. Em segundo lugar, a transformada z nos fornece informações sobre a estabilidade do filtro, mas isso será explicado mais tarde.

7.1

A Transformada z

Por definição, a transformada z de um sinal discreto x[n] é

em que z é um número complexo, z = rej␻. Tipicamente, n possui um intervalo finito – por exemplo, imagine um sinal x com seis valores. Efetivamente, nesse exemplo, n possui um intervalo de somente 0 a 5, pois qualquer valor de x antes do índice 0 ou após o índice 5 é considerado igual a 0. Assim, a transformada z seria:

Teoricamente, entretanto, para um x[n] geral, X(z) poderia divergir, pois n seria ⬁. A título de exemplo, considere uma função simples de um valor constante, tal como x[n] = 1, onde −⬁ < −n< − ⬁. Podemos expressar a transformada z de forma compacta com a equação mas essa soma claramente poderia ser infinita. Por tal razão, incluímos uma região de convergência (region of convergence – RoC) com a transformada z. Um aspecto interessante da RoC é que ela depende exclusivamente de r, e não de ␻. Lembre-se de que z = rej␻, de modo que z−k = r–ke−jk␻, e, independentemente do valor de ␻, o valor para e−jk␻ será delimitado pelos valores máximo e mínimo das funções cosseno e seno. Ou seja, e−jk␻ = cos(k␻) − isin(k␻), e as funções cosseno e seno retornam valores entre −1 e 1 (a magnitude de e–jk␻ é 1). Portanto, e−jk␻ não contribuirá para o fato de a transformada z ser infinita: ou o termo r−k torna a transformada z infinita, ou a transformada z converge. Deste modo, a região de convergência é circular [25]. Se soubermos que um ponto se encontra dentro (ou fora) da região de convergência, todos os demais pontos de mesma magnitude também estarão dentro (ou fora) da região de convergência. 209

Weeks 007.indd 209

27/04/12 20:36

210

CAPÍTULO 7

A RoC incluirá toda a área sobre e no interior do círculo unitário, para todos os casos práticos. Essa questão só se torna confusa quando consideramos todos os casos teóricos. Na verdade, alguns textos sobre DSP procuram evitar os casos em que a RoC não é o círculo unitário. Também podemos encarar a transformada z como uma série geométrica. Por exemplo, se c = / 0 e −1 < d < 1 [22]:

Deste modo, se x[n] = 1 para todo n > − 0,

Naturalmente, devemos verificar se essa soma converge ou não. Na realidade, quando |z−1| > − 1, a soma não converge (e não devemos utilizar a expressão fracionária anterior). Isso é fácil de constatar: à medida que n aumenta, acrescentamos outro termo cdn à nossa soma. Se por acaso d for uma fração de 1, o termo que adicionamos é menor que o último que acrescentamos, e a soma não cresce substancialmente. Considere, por exemplo, d = ½. O acréscimo de um milionésimo termo significaria o acréscimo de c × (½)1000000, ou c/21000000, à soma, o que não causaria nela uma alteração importante (especialmente se a compararmos com o efeito do acréscimo do primeiro termo, c). Contudo, se supusermos que d é exatamente igual a 1, o acréscimo de um milionésimo termo significará o acréscimo de c à soma. Isso será tão significativo quanto o acréscimo do primeiro termo. Se d for maior que 1, cada valor de cdn corresponderá ao acréscimo de um valor maior que o anterior, o que significa que a soma continuará a crescer continuamente. Tal como em uma fração, sabemos que a expressão X(z) = 1/1−z−1 é igual a zero quando o numerador (a parte superior da fração) é zero (embora não seja possível isso acontecer neste caso). Além disso, sabemos que a fração será infinita quando o denominador (a parte inferior) for igual a zero. Isso acontece quando 1−z−1 = 0, ou z−1 = 1. Essa é a ideia por trás da região de convergência, e nós voltaremos a ela quando examinarmos a resposta de frequência de um filtro.

7.2

Substituição de Dois Filtros FIR em Série

Quando dois filtros são colocados em série, a saída é uma convolução da entrada com os coeficientes do primeiro filtro, seguida pela convolução com os coeficientes do segundo filtro. Assim, é possível substituir os dois filtros por um único filtro? A resposta é sim, dois filtros podem ser combinados em um único filtro por meio da convolução de seus coeficientes de filtro. A Figura 7.1 ajuda a demonstrar essa ideia. O segundo filtro exibe pontos de interrogação em vez dos coeficientes de filtro, mas em breve aprenderemos a determinar esses valores.

Entrada

Saída a, b, c, d

x[n]

f, g, k, m w[n]

Entrada x[n]

???

y[n] Saída y[n]

Figura 7.1 Filtros FIR em série podem ser combinados.

Weeks 007.indd 210

27/04/12 20:36

A Transformada z

211

Isso traz duas consequências interessantes. Em primeiro lugar, a combinação de filtros em série pode economizar espaço caso o projeto seja implementado em hardware. Em segundo lugar, ela sugere que podemos fazer o oposto. Pode ser uma boa ideia decompor um filtro em dois filtros em série, de modo que os filtros resultantes sejam mais fáceis de criar. Suponha, por exemplo, que haja dois filtros FIR em série, com coeficientes {2, 0, 0.5} e {1, −1, 3}, respectivamente. O efeito é o mesmo obtido com um único filtro FIR com coeficientes {2, −2, 5.5, −0.5, 1.5}. Mas qual das opções é mais fácil de implementar, os dois primeiros filtros ou o equivalente? Note que o primeiro filtro pode ser criado sem multiplicadores; tudo de que precisamos é um deslocador. Para números de ponto fixo, um deslocamento para a esquerda equivale à multiplicação por 2, enquanto um deslocamento para a direita equivale à divisão por 2. Para o segundo filtro, nenhum multiplicador é necessário para a multiplicação por 1, e a multiplicação por −1 pode ser feita trocando-se o bit de sinal. A multiplicação por 3 é a única mais complicada e pode ser feita com um deslocamento e uma soma, ou seja, x + 2x = 3x. Para um sistema com apenas esses coeficientes, os dois primeiros filtros podem ser implementados sem multiplicadores. Vamos examinar como podemos encontrar os coeficientes combinados resultantes, da seguinte maneira: primeiramente, escreveremos as equações que descrevem a saída dos dois filtros alinhados na parte superior da Figura 7.1, como se fossem dois filtros separados; ou seja, um filtro com coeficientes {a, b, c, d} e uma entrada rotulada como x e uma saída rotulada como ␻, e outro filtro com coeficientes {f, g, k, m} com entrada ␻ e saída y. Consideraremos todos os coeficientes de filtro constantes.

Utilizamos ␻ como um rótulo apenas para tornar essa descrição menos confusa, mas o que realmente precisamos é de uma saída final y em termos da entrada original x. Para resolver isso, devemos ter uma expressão para ␻[n − k], em vez de ␻[n]. Na próxima etapa utilizamos a expressão para ␻[n], mas substituindo n por n − k.

Colocamos a saída y[n] em termos da entrada, x[n]:

Simplificamos:

Constatamos que nossa expressão para y[n] agora contém apenas uma combinação linear da entrada (e versões atrasadas dela), multiplicada por constantes. Os coeficientes resultantes são, portanto, {af, (ag + bf), (ak + bg + cf), (am + bk + cg + df), (bm + ck + dg), (cm + dk), dm}. Esse padrão é idêntico à convolução de {a, b, c, d} com {f, g, k, m}. Na verdade, a convolução nos fornece diretamente a resposta. Encontramos um filtro resultante utilizando convolução. Na seção a seguir, veremos como essa análise poderia ser feita com a transformada z.

7.3

Reexame da Combinação Sequencial de Filtros com z

Vamos retornar ao exemplo da Figura 7.1. Precisamos substituir os dois filtros h1[n] = {a, b, c, d} e h2[n] = {f, g, k, m} por um único filtro equivalente. Primeiro, podemos escrever a transformada z para esses filtros

Weeks 007.indd 211

27/04/12 20:36

212

CAPÍTULO 7

direta e simplesmente examinando seus coeficientes. Começamos com a definição (onde utilizamos H e h no lugar de X e x), e

expandimos a soma,

e substituímos os termos h[n] pelos valores anteriores definidos.

Pelo mesmo processo, encontramos H2(z).

Para encontrar o filtro equivalente, multiplicamos as transformadas H1(z) e H2(z) entre si. A razão para tal é que a multiplicação no domínio da frequência equivale à convolução no domínio do tempo, conforme demonstrado na Seção 7.7. Chamaremos a transformada z para nosso terceiro filtro de H3(z),

Simplificamos:

Combinamos os termos:

Esse resultado deve parecer familiar por ser o mesmo que encontramos para y[n], exceto pelo fato de x[n] não ser mencionado. Em vez disso, temos z−k para indicar os retardos, ou seja, quando H3(z) é aplicado à entrada X(z), temos a equação:

Por exemplo, X(z)z−1 no domínio do tempo significa que x[n] é atrasado em uma unidade ou que afX(z) = afx[n], (ag + bf)X(z)z−1 = (ag + bf)x[n − 1], (ak + bg + cf)X(z)z−2 = (ak + bg + cf)x[n − 2], e assim por diante. A próxima seção explica como z−1 corresponde a um retardo por uma unidade.

Weeks 007.indd 212

27/04/12 20:36

A Transformada z

7.4

213

Por que z−1 É o Mesmo que um Retardo por Uma Unidade?

Uma unidade de retardo frequentemente é mostrada como z−1 em uma caixa, já que a multiplicação por z−1 no domínio da frequência atrasa um sinal por uma amostra. Para verificarmos se é o caso, vamos examinar dois filtros FIR semelhantes, um com coeficientes {1, 0} e o outro com coeficientes {0, 1}. Esses filtros comuns são mostrados na Figura 7.2.

1 Entrada

y1[n]

x[n]

D

Saída

0

0 Entrada

y2[n]

x[n]

D

Saída

1

Figura 7.2 Dois filtros FIR comuns.

Por inspeção, vemos que o primeiro filtro não faz efetivamente nada e que a saída é igual à entrada: y1[n] = x[n]. O segundo filtro, na prática, também nada faz, exceto pelo fato de a saída ser uma versão atrasada da entrada, ou seja, y2[n] = x[n − 1]. Podemos remover os multiplicadores, pois a multiplicação por zero nos fornece um resultado zero, e a multiplicação de um número por um nos retorna o mesmo número. Como uma das entradas do somador é zero, podemos remover também os somadores do diagrama. Isso nos deixa com uma forma bastante reduzida desses dois filtros, conforme vemos na Figura 7.3.

Entrada

Entrada

x[n]

y1[n]

x[n]

y2[n]

Saída

Saída

D

Figura 7.3 Dois filtros FIR comuns, reduzidos.

O primeiro filtro possui uma função de transferência H1(z) = 1z0 + 0z−1, enquanto o segundo possui H2(z) = 0z0 + 1z−1. Se escrevermos a saída em termos da função de transferência e da entrada, teremos:

Weeks 007.indd 213

27/04/12 20:36

214

CAPÍTULO 7

Aqui vemos que a única diferença entre a transformada z do primeiro sistema e a do segundo sistema (atrasada em uma amostra) é que a potência à qual z é elevado é diminuída em uma unidade. Em outras palavras, um retardo de uma unidade de tempo equivale à multiplicação no domínio da frequência por z−1.

7.5

Como a Transformada z Reduz-se à Transformada de Fourier

Afirmamos anteriormente que a transformada discreta de Fourier é uma versão simplificada da transformada z. Nesta seção, isto será demonstrado. Antes de mais nada, começaremos pela definição da transformada z.

Até este ponto, temos utilizado z como um simples parâmetro para a transformada z. Por definição, z é um número complexo.

A substituição de z por sua definição gera a seguinte equação:

Após a simplificação, obtemos:

Em seguida, consideremos ␪ = 2␲m/N, em que m e n são índices e N é a extensão do sinal x. Também consideramos o sinal x como causal, de modo que o primeiro valor de x começa no índice zero.

Se r = 1, então r−n = 1 para todos os valores de n. Em consequência:

Note que a equação anterior corresponde à definição para a DFT. Dizemos que a transformada z encontrase no domínio da frequência graças a essa ligação direta entre ela e a transformada de Fourier. Geralmente representamos a DFT como X[m] no lado esquerdo, simplesmente porque m é o único parâmetro variável. Algumas pessoas empregam a notação X(␻) no lado esquerdo pela mesma razão, especialmente durante a utilização de variáveis diferentes, tal como na equação a seguir.

Weeks 007.indd 214

27/04/12 20:36

A Transformada z

215

Conforme já vimos, contudo, muitas vezes não precisamos empregar a definição de z para utilizar a transformada z. Se restringirmos o valor de z ao círculo unitário, onde r = 1, obteremos a transformada de Fourier a partir da transformada z.

7.6

Potências de −z

Ao multiplicarmos potências de −z, podemos utilizar o quadro a seguir para fazer substituições. Isso acontece no projeto do filtro, ao se calcular a resposta de frequência de um canal. Vemos que os sinais de −z−k se alternam para valores pares e ímpares de k. Isso é conveniente quando incorporamos sobre/subamostradores à nossa arquitetura.

7.7

Demonstração da Equivalência da Convolução no Domínio do Tempo em Relação à Multiplicação no Domínio da Frequência

Nesta seção demonstramos que a convolução no domínio do tempo fornece os mesmos resultados que os da multiplicação no domínio da frequência. Em outras palavras,

Vamos partir do princípio de que estamos lidando com pequenos sinais. Consideremos h = {h0, h1, h2} e x = {x0, x1, x2, x3}. Primeiramente, calculamos a convolução x * h conforme mostrado na Tabela 7.1. (A título de exercício, calcule h * x e mostre que resultado obtido é o mesmo.) Deve ficar claro aqui que mais (ou menos) valores para x ou h afetariam este exemplo. Caso contrário, experimente este exemplo por conta própria com x0, ..., x4 e depois novamente com h0, ..., h3.

Tabela 7.1

x0 h0 x 0 h0

x 0 h0

x1 h1 x 1 h0 x 0 h1 x 1 h0 + x 0 h1

Convolução de x e h.

x2 h2 x 2 h0 x 1 h1 x 0 h2 x 2 h0 + x 1 h1 +x 0 h 2

x3 x 3 h0 x 2 h1 x 1 h2 x 3 h0 + x 2 h1 +x 1 h 2

x 3 h1 x 2 h2 x 3 h1 + x 2 h2

x 3 h2 x 3 h2

Agora vamos calcular os valores para X(z) e H(z). Pela definição da transformada z, temos:

Weeks 007.indd 215

27/04/12 20:36

216

CAPÍTULO 7

Multiplicamos um pelo outro:

Agora podemos simplificar os termos, especialmente as potências de z. Lembre-se de que z−a × z−b = Deste modo, temos:

z−a−b.

O próximo passo é agrupar os termos pelo expoente de z.

Esse resultado pode ser interpretado lembrando-se como z−1 corresponde a um retardo em uma unidade de tempo. Deste modo, a primeira saída de um sistema que computa X(z) × H(z) seria, no domínio do tempo, x0h0, seguido de x1h0 + x0h1 + x2h0 + x1h1 + x0h2 etc. — os mesmos valores na mesma ordem que a do exemplo de convolução. Outra forma de constatar que os resultados são equivalentes seria executar a transformada z na saída do exemplo de convolução, o que nos dará x0h0z−0, (x1h0 + x0h1) z−1, e assim por diante. Deste modo, estabelecemos que a convolução no domínio do tempo é equivalente à multiplicação no domínio da frequência.

7.8

Resposta de Frequência de Filtros

A transformada z é uma ferramenta analítica útil que revela informações sobre a estabilidade de um filtro. No Capítulo 3, “Filtros”, vimos que é possível a saída de um filtro IIR aproximar-se do infinito (ou infinito negativo), com base em seus coeficientes. Isso pode ser constatado com a função de transferência, H(z). A função de transferência nos fornece a resposta do filtro, com base em seus coeficientes. Ela nos permite examinar o efeito do filtro em geral sem termos de fornecer uma entrada específica. Vimos anteriormente a transformada z da função impulso unitário, H(z), aplicada como uma operação à entrada X(z), para gerar a saída Y(z). Em outras palavras,

Agora viramos isso ao contrário e definimos a função de transferência, H(z), da seguinte maneira:

Entretanto, existe uma diferença importante aqui. As seções anteriores utilizavam filtros FIR, e a definição aqui se aplica também a filtros IIR.

Weeks 007.indd 216

27/04/12 20:36

A Transformada z

217

Sob as circunstâncias certas (ou seja, valores para z), o denominador da função de transferência poderia ser igual a zero. Se isso acontecer, nossa resposta de frequência, H(z), será infinita. Tal valor para z recebe o nome de polo, uma imagem emprestada de uma tenda de circo, cuja lona apresenta uma forma pronunciada a partir dos polos, tal como um traçado tridimensional da superfície de H(z) apresentaria para determinados valores de z. Além disso, alguns valores de z poderiam resultar em um valor zero para a função como um todo, a partir do numerador. Chamamos esses pontos de zeros. Quando falamos a respeito da resposta de frequência para filtros, estamos interessados principalmente nas localizações dos zeros e dos polos. Se um polo estiver localizado fora de nossa região de convergência, teremos um problema: o sistema será instável. Muitas vezes, os zeros e os polos são traçados no plano z, o que nos permite visualizar H(z) de cima. A função zplane em MATLAB fará isso para nós. A função de transferência H(z) descreve a resposta de frequência dos filtros, encontrada a partir da relação da transformada z dos coeficientes de filtro. Para um filtro FIR, os coeficientes de filtro b[k] são idênticos à resposta ao impulso unitário h. Essa é a razão pela qual utilizamos h[k] em letras minúsculas para os coeficientes de filtro de um filtro FIR e H(z) para a função de transferência. Portanto, encontramos a resposta de frequência para um filtro FIR com:

Para filtros IIR, temos a mesma ideia, mas é um pouco mais complicado encontrar a função de transferência, pois temos dois conjuntos de coeficientes de filtro. Primeiramente, vamos prosseguir com os filtros FIR. Suponha que tenhamos três coeficientes de filtro, b0, b1 e b2, correspondendo à resposta ao impulso unitário de h0, h1 e h2. Podemos escrever a relação descrevendo a saída como:

A transformada z possui a propriedade distributiva em relação à adição, o que significa que podemos tratar separadamente cada porção aditiva em uma equação. Lembre-se de que uma versão atrasada da entrada, como x[n − 1], tem X(z)z−1 como transformada z, o que facilita a aplicação da transformada. Se encontrarmos a transformada z da relação anterior, teremos:

Simplificamos:

Se dividirmos X(z), obteremos

o que vem a ser a mesma coisa que a transformada z dos coeficientes de filtro, ou H(z) por definição, visto que hk = bk para todos os valores k inteiros. Embora tenhamos essa análise para alguns coeficientes somente, deve ficar claro que isso também vale para um filtro FIR com N + 1 coeficientes. Portanto, podemos escrever a função de transferência para um filtro FIR como

Para um filtro IIR, a situação é um pouco mais complicada, pois temos tanto uma parte de alimentação direta (coeficientes b[k]) quanto uma parte de realimentação (coeficientes a[k]). Nossa abordagem é a mesma. Escrevemos a relação entrada/saída, encontramos a transformada z correspondente e, em seguida, reorganizamos os termos até obtermos Y(z)/X(z) em um lado. Deste modo, obtemos uma expressão no outro lado para a função de transferência H(z).

Weeks 007.indd 217

27/04/12 20:36

218

CAPÍTULO 7

Vamos examinar um caso geral, para alguns coeficientes somente. Suponha que um filtro IIR possui coeficientes de alimentação direta {b0, b1, b2} e coeficientes de realimentação {a1, a2}. O que é a função de transferência? Onde estão os zeros? Onde estão os polos? Trataremos dessas questões uma de cada vez. Primeiramente, escrevemos a função no domínio do tempo que descreve a entrada/saída:

Encontramos a transformada z:

Agrupamos os termos:

Reorganizamos:

Visto que H(z) = Y(z)/X(z) por definição, acabamos de encontrar nossa função de transferência. Isso funcionará para tantos coeficientes de alimentação direta (N + 1) e de realimentação (M) quanto desejarmos. Temos N + 1 coeficientes de alimentação direta simplesmente porque começamos a contá-los a partir de zero. Lembre-se de que a parte de realimentação começa com a[1] em vez de a[0], pois não há multiplicador naquela posição correspondente (utilize a Figura 3.10 como referência). Assim, começamos a contar os coeficientes de realimentação a partir de um, para um total de M. Deste modo, temos a seguinte função de transferência:

Em seguida, para encontrar os zeros, definimos o numerador como 0:

Se colocarmos isso em termos de expoentes positivos para z:

Se utilizarmos a fórmula quadrática, descobriremos que as raízes são:

e

Uma vez fornecidos valores reais no lugar dessas variáveis, podemos calcular uma resposta numérica. Encontramos os polos de forma semelhante. Primeiro, definimos o denominador como 0:

Se colocarmos isso em termos de expoentes positivos para z, teremos:

Weeks 007.indd 218

27/04/12 20:36

A Transformada z

219

Ao utilizarmos a fórmula quadrática, encontramos as raízes, observando que a0 = 1, ao mesmo tempo negativando os valores a1 e a2:

e

Para resumir, encontraremos zeros e polos utilizando essas técnicas em um exemplo específico.

Exemplo 7.1 Suponha que dispomos de um filtro IIR com coeficientes de alimentação direta {4, 5, 6} e coeficientes de realimentação {2, 3}. Qual é a função de transferência? Onde estão os zeros? Onde estão os polos? Resposta: Encontramos a função de transferência tal como fizemos anteriormente. Primeiro, escrevemos a função no domínio do tempo, descrevendo a entrada/saída:

Encontramos a transformada z:

Agrupamos os termos:

Reorganizamos:

Como H(z) = Y(z)/X(z) por definição, encontramos nossa função de transferência:

Em seguida, para encontrar os zeros, definimos o numerador como 0:

Se colocarmos isso em termos de expoentes positivos para z, teremos:

Ao utilizarmos a fórmula quadrática, encontramos as raízes:

e

Weeks 007.indd 219

27/04/12 20:36

220

CAPÍTULO 7

A seguir, uma sessão de MATLAB que mostra os cálculos: >> B = [4, 5, 6]; >> zero1 = (-B(2) + sqrt(B(2)^2 - 4*B(1)*B(3)))/(2*B(1)) zero1 = -0.6250 + 1.0533i >> zero2 = (-B(2) - sqrt(B(2)^2 - 4*B(1)*B(3)))/(2*B(1)) zero2 = -0.6250 - 1.0533i Agora voltamos nossa atenção para os polos. O primeiro passo é definir o denominador como 0:

Agora multiplicamos todos os componentes por z2 para obtermos expoentes positivos para z:

Novamente obtemos as raízes a partir da fórmula quadrática:

e

No que tange à computação, utilizamos MATLAB: >> A = [1, -2, -3]; >> pole1 = (-A(2) + sqrt(A(2)^2 - 4*A(1)*A(3)))/(2*A(1)) pole1 = 3 >> pole2 = (-A(2) - sqrt(A(2)^2 - 4*A(1)*A(3)))/(2*A(1)) pole2 = -1 Em resumo, temos dois zeros nos valores z {−0.6250 ± 1.0533j} e dois polos em {3, −1}. Por fim, podemos traçar os zeros e polos com o comando a seguir (se o computador possuir a caixa de ferramentas de processamento de sinal do MATLAB): >> zplane([4, 5, 6], [1, -2, -3]); A Figura 7.4 mostra o traçado gerado pelo comando anterior. Note que o parâmetro é um vetor linha contendo os coeficientes de alimentação direta do filtro. O segundo parâmetro é 1, seguido pelas versões negativadas dos coeficientes de realimentação do filtro. A razão pela qual eles são negativados e precedidos

Weeks 007.indd 220

27/04/12 20:36

A Transformada z

221

por 1 tem a ver com a função de transferência: esses vetores não são os coeficientes de filtro, mas as constantes dos polinômios da função de transferência. Naturalmente, a função de transferência depende diretamente dos coeficientes de filtro, de modo que vemos os mesmos números (2 e 3) aparecerem em ambos. Note que a função zplane requer que os parâmetros sejam vetores de linha, caso contrário eles não serão interpretados como o numerador e o denominador da função de transferência. Vemos que a Figura 7.4 nos fornece graficamente a mesma informação que calculamos, com dois zeros em {−0.6250 ± 1.0533j} e dois polos em {3, −1}. Como temos um polo fora do círculo unitário, esse filtro IIR é instável.

Zeros e polos para um filtro IIR de exemplo 1.5

Parte imaginária

1 0.5 0 – 0.5 –1 – 1.5 –1

–0.5

0

0.5

1 1.5 Parte real

2

2.5

3

Figura 7.4 Exemplo de traçado de zeros e polos.

Um atributo oportuno dos filtros FIR é que, como não há realimentação, eles não possuem polos. Assim, podemos utilizar a função de transferência do filtro IIR para o FIR também.

O denominador reduz-se de onde não há valores ak, para 1 − 0, ou simplesmente um. Isso advém da maneira como a função de transferência H(z) é definida. Ela especifica como o filtro mapeia a entrada para a saída, sem especificar a entrada. Ocasionalmente a saída do filtro será zero, tal como quando a entrada for zero. Por exemplo, se a entrada x[n] for 0 para todo n, qualquer filtro FIR igualmente produzirá zeros, ou seja, y[n] = b[0]x[n] + b[1]x[n − 1] + ... + b[k]x[n − k]. Desde que os valores x[ ] sejam iguais a zero, os valores y[ ] também o serão. Isso também pode ser expresso no domínio da frequência com a transformada z, como Y(z) = H(z)X(z). Se x[n] for 0 para todo n, então X(z) = 0z0 + 0z−1... + 0z−n, ou X(z) = 0. H(z) especifica o efeito do filtro, de modo que H(z) = b0z0 + b1z−1 + ... + bkz−k. A partir da equação de entrada/saída, Y(z) = H(z) vezes 0 para esse X(z) em particular, de modo que Y(z) também será zero. O fato de a função de transferência H(z) possuir zeros indica simplesmente que existem algumas condições em que H(z) pode ser zero, o que significa que Y(z) será zero também. Por definição, um polo é um valor para z com o qual a função de transferência torna-se infinita. Qualquer quantidade z−k pode ser considerada 1/zk e, se z = 0, esse valor é infinito. Assim, para cada ocorrência de z−k no numerador, consideraríamos que ela teria um polo correspondente em z = 0. Essa é

Weeks 007.indd 221

27/04/12 20:36

222

CAPÍTULO 7

a razão pela qual a função zplane traçará polos mesmo que definamos o denominador como 1, ou seja, não há realimentação. Isso significa que um filtro FIR é instável para a frequência zero? Não, não é esse o caso. A frequência zero significa que a entrada nunca muda, que temos uma repetição interminável de algum valor na entrada. Mas um valor z = 0 significa que a magnitude (r de rej␻) possui um valor zero; o ângulo ␻ (frequência) pode ser de qualquer valor. Em outras palavras, se a frequência for igual a zero e a magnitude for diferente de zero, o filtro será estável. Como um filtro FIR pode se tornar instável? Isso só acontece na impraticável eventualidade de ele possuir um número infinito de coeficientes de filtro, situação em que sua transformada z corresponderá a uma soma infinita.

7.9

Tendências de um Filtro IIR Simples, Parte II

Lembra-se de quando examinamos a saída de um filtro IIR simples (veja a Figura 3.29)? Vimos que o coeficiente de realimentação a representou um papel fundamental na determinação do comportamento da saída. Agora que dispomos da transformada z, podemos prever o comportamento com base em a. Este exemplo fornece a equação de entrada/saída como:

Ao tirarmos a transformada z, obtemos:

Vemos que existe um polo em z = a e que esse polo repousa no interior do círculo unitário quando |a| < 1. Isso proporciona a estabilidade que vimos na parte superior da Figura 3.29. Observamos a estabilidade condicional nos dois traçados centrais e casos instáveis nos dois traçados inferiores.

RESUMO . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Este capítulo apresenta a transformada z como uma generalização da transformada de Fourier. É útil por falar sobre a resposta de frequência dos filtros e se um filtro IIR é estável ou não. Um filtro FIR é estável para todas as finalidades práticas (o que exclui quaisquer sistemas teoricamente possíveis, mas impossíveis de criar, tal como aquele com um número infinito de taps). A transformada z permite que calculemos o efeito de um filtro em uma entrada sem termos de especificá-la, o que recebe o nome de função de transferência H(z). Temos zeros e polos, correspondendo a uma resposta de função de transferência de zero ou ±⬁. Uma boa maneira de encarar H(z) é como uma função bidimensional que nos fornece uma superfície, com base na variável z bidimensional (complexa). Este capítulo demonstrou que a multiplicação por z−1 corresponde a um retardo por uma unidade de tempo. Vimos também neste capítulo que a multiplicação no domínio da frequência equivale à convolução no domínio do tempo. Existe uma transformada z inversa, mas ela está fora do escopo deste texto. Para maiores informações, consulte The Electrical Engineering Handbook, de W. K. Chen (ed.), [29].

EXERCÍCIOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1. Qual é a transformada z de x = {3, 2, 4}? 2. Um filtro FIR possui os seguintes coeficientes de filtro: {1, −1}. Qual é a resposta de frequência? Onde os zeros estão localizados? Utilize MATLAB para calcular a saída com uma entrada cos(2␲100t), amostrada a fs = 200 amostras/segundo.

Weeks 007.indd 222

27/04/12 20:36

A Transformada z

223

3. Um filtro FIR possui os seguintes coeficientes de filtro: {1, 1}. Qual é a resposta de frequência? Onde os zeros estão localizados? Utilize MATLAB para calcular a saída com uma entrada cos(2␲100t), amostrada à fs = 200 amostras/segundo. 4. Um filtro possui a seguinte função de transferência:

(a) Determine onde estão os zeros e os polos. (b) Trace-os (manualmente) no plano z com um x para um polo e um o para um zero. Esse filtro é estável? (c) Desenhe o filtro em termos de multiplicadores, somadores e unidades de retardo. 5. Qual é a função de transferência, H(z), para os filtros a seguir? (a) Filtro FIR com coeficientes b[k] = {−17, 29, 107, 62} (b) Filtro IIR com coeficientes de alimentação direta b[k] = {−17, 29, 107, 62} e coeficientes de realimentação a[k] = {4, −7, −26, −15} 6. Onde os zeros e polos estão localizados para as funções de transferência a seguir? Esses filtros são estáveis? (a)

(b) (c) 7. Suponha que tenhamos um filtro com a função de transferência a seguir. Qual é o valor de H(z) quando z = 2e j ␲/6? E quando z = 2e−j2␲/6? Encontre os valores analiticamente e reduza a resposta a um único valor complexo. Em seguida, escreva um programa para demonstrá-lo.

PROJETO . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Suponha que tenhamos um filtro IIR com coeficientes de alimentação direta [0.3, 0.7, 1, 0.7, 0.3] e coeficientes de realimentação [a1, a2, a3]. Considere que os valores an variam de –2 a +2. Quais valores resultam em um filtro estável? Dica: Examine o programa using_zplane.m. Um exemplo de solução é fornecido no site da LTC Editora, como project7.m. Observe que você precisará da caixa de ferramentas de processamento de sinal do MATLAB para a função zplane. Caso contrário, você poderá criar sua própria versão dessa função desenhando uma imagem de um círculo unitário. O exemplo de solução também utiliza a função roots do MATLAB e determina a estabilidade sem a necessidade de gerar um traçado. Tente resolver este problema por conta própria antes de ler o texto a seguir.

O modo mais fácil de resolver este problema utiliza a função zplane. Definimos as listas A e B, respectivamente, representando os coeficientes de realimentação e de alimentação direta. Em seguida, chamamos a função. zplane(B, A); A posição dos polos no traçado final nos informa se o conjunto de valores resulta ou não em um filtro estável.

Weeks 007.indd 223

27/04/12 20:36

224

CAPÍTULO 7

Já conhecemos o conjunto de coeficientes de alimentação direta, por terem sido especificados. B = [0.3, 0.7, 1, 0.7, 0.3]; Para os coeficientes de realimentação, podemos experimentar todas as possibilidades de um subconjunto. Esses coeficientes devem ser discretos, e precisamos de um número razoável deles. Uma forma de lidar com isso consiste em definir loops for aninhados para os três coeficientes de realimentação, da seguinte maneira: for a1 = -2:step:2 for a2 = -2:step:2 for a3 = -2:step:2 % posicao para outro codigo... end end end A variável step deve ser definida antecipadamente. Se a definirmos como um, haverá cinco possibilidades para cada coeficiente, ou 125 permutações. Caso você deseje ver o programa trabalhando em um teste mais curto, experimente definir step como dois. Ainda há alguns detalhes a serem considerados. A função zplane do MATLAB não permite que o coeficiente principal seja igual a zero. Assim, verificamos tal condição antes de chamarmos essa função. Além disso, precisamos fazer uma breve pausa após a chamada de zplane, para poder examinar os resultados. O código que corresponde ao loop mais interno parece-se com este: if (a1 ˜= 0) A = [a1, a2, a3]; disp(A); zplane(B, A); pause(0.2); end Criamos a lista A com base nos três índices de loop, digitamos os mesmos na janela de comando, exibimos o traçado com a função zplane e aguardamos dois décimos de segundo antes de prosseguirmos. Até aqui dispomos de uma solução, mas que não é muito amigável. Se a caixa de ferramentas de processamento de sinal não estiver disponível, essa solução não funcionará. Mas a função roots do MATLAB nos fornece outra possibilidade. Sabemos que os polos são encontrados pelas raízes dos coeficientes de realimentação. Sequer precisamos dos coeficientes de alimentação direta para determinar a estabilidade. Portanto, podemos encontrar as raízes e examiná-las. Precisamos apenas de uma raiz fora do círculo unitário para classificar o sistema como instável, de modo que podemos encontrar o máximo do valor absoluto das raízes. Outra forma de encararmos isso é: encontramos as raízes, obtemos os valores absolutos (para poder saber a que distância estão da origem) e examinamos o mais distante. Se for menor que um, então todos eles deverão estar necessariamente no interior do círculo unitário e, portanto, os coeficientes resultarão em um sistema estável. r = max(abs(roots(A))); if (r < 1) stability = 'estavel'; elseif (r > 1) stability = 'instavel'; else stability = 'condicionalmente estavel'; end

Weeks 007.indd 224

27/04/12 20:36

A Transformada z

225

Aqui, encontramos a raiz de maior magnitude, comparamos com um e definimos a variável stability com a sequência de caracteres apropriada. Em seguida, informamos ao usuário o que encontramos. disp(sprintf(' %4.1f %4.1f %4.1f

%s', A, stability));

Isso imprimirá uma lista dos valores verificados e a estabilidade de cada conjunto. Por fim, podemos utilizar a função zplane com isso, caso esteja disponível para o usuário. Podemos determinar facilmente isso pelo código a seguir. haveit = which('zplane'); if (length(haveit) == 0) WeHaveZPlane = false; % nao encontrado else WeHaveZPlane = true; % encontrado end A primeira linha chama a função which, que retorna informações sobre o programa passadas como parâmetro. Você pode notar que ela lembra o comando Unix de mesmo nome. Nossa variável haveit será uma longa sequência de caracteres especificando a localização da função zplane ou uma sequência vazia. Em seguida, verificamos essa sequência de caracteres e decidimos se temos a função definindo uma variável booleana chamada WeHaveZPlane. Mais tarde, verificaremos esse valor antes de chamarmos a função zplane, como no código a seguir. if (WeHaveZPlane) zplane(B, A); pause(0.2); end Se o usuário não possuir a caixa de ferramentas necessária, o traçado não aparecerá. Mas, de qualquer modo, o programa será executado. O usuário será capaz de visualizar com rapidez o traçado para vários coeficientes de realimentação e determinar visualmente quais resultam em estabilidade, caso disponham da função zplane. O computador também imprime os coeficientes e faz sua própria verificação de estabilidade.

Weeks 007.indd 225

27/04/12 20:36

Weeks 007.indd 226

27/04/12 20:36

A Transformada Discreta de Fourier

8

A transformada discreta de Wavelet (DWT) é utilizada em diversas aplicações de processamento de sinais, tais como compressão de vídeo [30], compressão em comunicações via Internet [31], reconhecimento de objetos [32] e análise numérica. Ela pode representar eficientemente alguns sinais, especialmente aqueles com mudanças localizadas. Considere o exemplo de representação de uma função de impulso unitário com a transformada de Fourier, que requer uma quantidade infinita de termos por estarmos tentando representar uma única mudança rápida com uma soma de senoides. Entretanto, a transformada de wavelet pode representar esse sinal de curta duração com apenas alguns termos. Essa transformada surgiu de diferentes áreas, incluindo matemática, física e processamento de imagem. Basicamente, pessoas em diferentes áreas estavam fazendo a mesma coisa, mas empregando terminologias diferentes. No final da década de 1980, Stéphane Mallat unificou o trabalho em um único tema [1]. Desde então, a transformada de wavelet vem ganhando popularidade com aplicações como a compressão de imagens de impressão digital e o recente padrão JPEG2000 (JP2), que incorpora a análise multirresolução. A transformada de wavelet é discreta em tempo e escala. Em outras palavras, os coeficientes da DWT podem possuir valores reais (ponto flutuante), mas os valores de tempo (deslocamento) e escala utilizados para indexar esses coeficientes são inteiros. Este capítulo explica como a transformada de wavelet trabalha e mostra como a wavelet decompõe um sinal em sinais de detalhe e uma aproximação. Imagine nosso sinal de entrada passando através de um par de filtros FIR, como na Figura 8.1. Ao menos para um nível de resolução para um sinal unidimensional, essa é a transformada discreta de wavelet, presumindo-se que os coeficientes de filtro atendam determinados critérios. Chamamos esse nível de resolução de uma oitava, termo livremente emprestado da terminologia musical. Um filtro do par de análise (transformada) é um filtro passa-baixas (LPF) que implementa uma função de escalonamento, enquanto o outro é um filtro passa-altas (HPF) que corresponde à função wavelet (veja a Figura 8.1). Cada filtro é sucedido por um subamostrador, para tornar a transformada eficiente, ao menos em termos de memória. A Figura 8.2 mostra o par de síntese (transformada inversa de wavelet), consistindo em um filtro passabaixas inverso (ILPF, de inverse lowpass filter) e um filtro passa-altas inverso (IHPF, de inverse highpass filter), cada qual precedido por um sobreamostrador.

Entrada X[n]

LPF

2

HPF

2

Figura 8.1 Filtros de análise.

2

ILPF Saída

2

IHPF

Y[n]

Figura 8.2 Filtros de síntese.

227

Weeks 008.indd 227

02/05/12 16:40

228

CAPÍTULO 8

O filtro passa-baixas produz o sinal médio, enquanto o filtro passa-altas produz o sinal de detalhe. Por exemplo, um filtro passa-baixas simples pode ter coeficientes {½, ½}, produzindo saídas (x[n] + x[n − 1])/2, que claramente corresponde à média das duas amostras. Um filtro passa-altas simples correspondente teria coeficientes {½, − ½}, produzindo saídas (x[n] − x[n − 1])/2, metade da diferença das amostras. Isso deve lembrá-lo da transformada aditiva/subtrativa (plus-minus) do primeiro capítulo, que é um tipo simples de transformada de wavelet. Embora o sinal médio se parecesse muito com o original, precisamos dos detalhes que fazem o sinal reconstruído corresponder ao original. O MATLAB fornece funções para a dwt e a idwt na caixa de ferramentas wavelet. Elas executam a transformada direta e a transformada inversa, respectivamente. Vamos experimentar um exemplo. Primeiramente, definimos nosso sinal a partir dos valores de temperatura do exemplo do primeiro capítulo: x = [59, 65, 68, 70, 61, 61, 63, 66, ... 66, 67, 70, 71, 71, 70, 70, 70]; Em seguida, encontramos a transformada discreta de wavelet desses dados. Há muitas wavelets diferentes para escolher, e aqui selecionamos uma wavelet Daubechies curta denominada db2. [a, d] = dwt(x, 'db2'); A função dwt retorna uma aproximação (a) e um sinal de detalhe (d). A aproximação parece semelhante ao original, apenas em uma escala mais curta, com cerca de metade do número de amostras. O número real de saídas depende da extensão da função wavelet. A Figura 8.3 mostra o sinal original no gráfico superior, seguido pela aproximação no gráfico intermediário e pelo sinal de detalhe no gráfico inferior. Para obter o sinal detalhado, a função dwt aplica a wavelet escolhida aos dados fornecidos. A convolução fará isso para nós, tal como um filtro FIR fará. Em seguida, subamostramos os dados, ou seja, mantemos somente cada segundo valor (um sim, outro não). Sim, existem maneiras mais eficientes de se fazer isso, mas a filtragem seguida de uma subamostragem nos proporciona um modo fácil de encarar a transformada. O sinal de aproximação pode ser encontrado da mesma forma, passando o sinal original através de um filtro FIR e subamostrando os resultados em seguida. Utilizamos uma função especial de escalonamento para obter a aproximação e, tal como veremos, os coeficientes de filtro para a operação de aproximação são baseados nos coeficientes de filtro da wavelet. Após a transformada, temos dois sinais a e d. O tamanho dos dados (de a e d) é aproximadamente o mesmo do sinal original x. A partir desse ponto, não precisamos dos dados originais. Podemos analisar

Sinal original 70 65 60 2

4

6

8 10 Aproximação

12

14

16

110 100 90 80

1

2

3

4

5 Detalhe

6

7

8

9

1

2

3

4

5

6

7

8

9

2 0 –2 –4

Figura 8.3

Weeks 008.indd 228

O sinal original e sua aproximação e sinal de detalhe DWT.

02/05/12 16:40

A Transformada Discreta de Fourier

229

os resultados ou talvez utilizá-los em uma aplicação como a compressão. Quando desejamos recriar o sinal original, passamos a aproximação e os detalhes por uma transformada inversa, da seguinte maneira: x_hat = idwt(a, d, 'db2'); A transformada discreta de wavelet inversa precisa conhecer os sinais de aproximação e de detalhe, juntamente com a wavelet. Chamamos o resultado de x_hat, como um lembrete de que se trata de uma reconstrução do x original. O que obtemos como resultado? Vamos examinar os valores diretamente. x = Columns 1 through 7 59

65

68

70

61

61

63

71

71

70

Columns 8 through 14 66

66

67

70

Columns 15 through 16 70

70

Agora compare os valores originais com os fornecidos pela transformada inversa. x_hat = Columns 1 through 4 59.0000

65.0000

68.0000

70.0000

63.0000

66.0000

70.0000

71.0000

Columns 5 through 8 61.0000

61.0000

Columns 9 through 12 66.0000

67.0000

Columns 13 through 16 71.0000

70.0000

70.0000

70.0000

Constatamos que todos os valores coincidem. Com exceção de um erro de arredondamento ínfimo, temos uma reconstrução perfeita. A análise multirresolução alimenta o sinal médio em outro conjunto de filtros, o que gera os sinais médio e de detalhe na oitava seguinte [1]. Os sinais de detalhe são mantidos, mas as médias de oitava mais altas podem ser descartadas, pois podem ser recomputadas durante a síntese. As saídas de cada oitava possuem somente metade da quantidade de dados da entrada (mais alguns coeficientes devido ao filtro). Portanto, a representação da wavelet tem aproximadamente o mesmo tamanho que o original. A seguir, um exemplo que encontra a DWT para duas oitavas. Utilizamos o mesmo sinal de exemplo de antes. O comando dwt é muito semelhante, exceto pelo fato de as variáveis de saída serem renomeadas para a1 e d1, para a aproximação e o detalhe na primeira oitava. Em seguida, repetimos o comando dwt na aproximação (a1). Nesse ponto, não precisamos mais de a1 e o apagamos da memória.

Weeks 008.indd 229

02/05/12 16:40

230

CAPÍTULO 8

x = [59, 65, 68, 70, 61, 61, 63, 66, ... 66, 67, 70, 71, 71, 70, 70, 70]; [a1, d1] = dwt(x, 'db2'); [a2, d2] = dwt(a1, 'db2'); clear a1; Encontramos recursivamente a DWT ao utilizarmos a aproximação da primeira oitava como entrada para a função dwt quando a chamamos pela segunda vez. Os dados da segunda oitava nos fornecem uma aproximação e detalhes em uma escala maior – cada valor de aproximação na segunda oitava advém de diversos valores da aproximação da primeira oitava, que por sua vez advêm de diversos valores do sinal original. A aproximação da segunda oitava poderia ser chamada de uma aproximação do sinal original. Dizemos que cada aproximação advém de diversos valores, uma vez que ela depende da wavelet utilizada. A wavelet de Haar possui apenas dois coeficientes de filtro. A db2 possui quatro, mas um exemplo grande como db44 possui 88. Para recriarmos o sinal, precisamos de dois detalhes, d1 e d2, juntamente com a aproximação da segunda oitava, a2. Os subsinais a2 e d2 são mais curtos que as saídas da primeira oitava. Juntos, eles têm aproximadamente o tamanho da aproximação da primeira oitava. Em outras palavras, os dados que precisamos manter não crescem de maneira significativa. Em seguida, recombinamos os dados de nossa decomposição da wavelet. a1_hat = idwt(a2, d2, 'db2'); x_hat = idwt(a1_hat(1:length(d1)), d1, 'db2'); A primeira linha recria a aproximação da primeira oitava, a1_hat, a partir dos dados da segunda oitava. Em seguida, criamos x_hat com base nessa aproximação reconstruída e nos detalhes para a primeira oitava. Você pode ter percebido que utilizamos a1_hat(1:length(d1)) em vez de a1_hat. Se passarmos duas listas de extensões diferentes para a função dwt, obteremos um erro. E, como era de se esperar, a lista a1_hat possui um elemento a mais que d1, devido à natureza ambígua da subamostragem. Ou seja, suponha que subamostremos uma sequência e obtenhamos {1, 3}. Se mais tarde tentarmos recriar o sinal original com base nesses dois valores, o sinal recriado deverá ter três ou quatro valores? Em outras palavras, o sinal original era {1, 2, 3, 4} ou {1, 2, 3}? Não temos como saber. Por segurança, devemos retornar um número par de valores. Isso explica por que o MATLAB fornece uma amostra extra a a1_hat. Por fim, avaliamos o quanto a reconstrução se assemelha ao original. Calculamos o erro e o exibimos com 10 dígitos. err = sum(abs(x - x_hat)); disp(sprintf('Error is %10.9f', err)); Ao executarmos esse código, obtemos a seguinte resposta: Error is 0.000000000 Algum erro de arredondamento provavelmente ocorreu, mas nada significativo. Examine o programa simpleDWTexample.m, localizado no site da LTC Editora. Com esses exemplos, vemos como encontrar a DWT para um dado sinal unidimensional. A utilização da transformada discreta de wavelet em imagens (ou em outros dados bidimensionais) pode ser realizada com as funções dwt2 e idwt2 em MATLAB. Você pode criar sua própria transformada bidimensional de duas maneiras: 1) através da aplicação dos filtros passa-baixas e passa-altas ao longo das linhas de dados e da posterior aplicação de cada um desses filtros ao longo das colunas dos resultados anteriores; ou 2) através da aplicação de quatro convoluções matriciais, uma para cada combinação passa-baixas/passaaltas, horizontal/vertical (ou seja, baixo-horizontal e baixo-vertical, baixo-horizontal e alto-vertical etc.). A separabilidade significa que a transformada bidimensional é simplesmente uma aplicação da DWT unidimensional nas direções horizontal e vertical [33]. A transformada bidimensional inseparável trabalha de forma um pouco diferente, pois calcula a transformada com base em uma amostra bidimensional da entrada convolvida com uma matriz, mas os resultados são os mesmos. Pense nisso como duas formas de chegar

Weeks 008.indd 230

02/05/12 16:40

A Transformada Discreta de Fourier

231

à mesma resposta; assim como podemos combinar dois filtros em um (consulte a Seção 7.2), podemos combinar operações horizontais e verticais em uma multiplicação matricial. A principal diferença entre as DWT unidimensional, bidimensional e tridimensional é que um par de filtros opera em cada canal para cada dimensão. Os números correspondentes não devem ser tomados ao pé da letra, pois há modos mais eficientes de implementar todas as três arquiteturas [34]. Este capítulo examina como os filtros FIR podem ser utilizados para executar a transformada de wavelet. Primeiramente, examinaremos a transformada de Haar, desenvolvida por Alfred Haar antes que o termo wavelet tivesse se estabelecido [35]. Em seguida, examinaremos a transformada de wavelet com a wavelet de Daubechies de quatro coeficientes [2]. As wavelets de Daubechies são na prática uma generalização da transformada de Haar, e às vezes a transformada de Haar é tratada como uma wavelet de Daubechies de dois coeficientes. A Daubechies efetivamente possui uma família de wavelets, empregando o padrão TCFB, só que com mais coeficientes.

8.1

O Banco de Filtros de Dois Canais

Para demonstrarmos a transformada geral, utilizamos a Figura 8.4. Aqui, um sinal de entrada alimenta dois canais, cada qual com um par de filtros FIR. Chamamos essa estrutura de um banco de filtros de dois canais. Note que z[n] simplesmente rotula um subsinal, sem ter qualquer relação com a transformada z. Da mesma forma, w[n] rotula os dados intermediários no canal inferior. A metade esquerda da Figura 8.4 executa a transformada direta, também chamada de análise, enquanto a metade direita corresponde à transformada inversa, também chamada de síntese. Quando falamos de uma transformada como a de Haar ou a de wavelet, nos referimos somente à parte da análise, a metade esquerda da figura. Por outro lado, a transformada inversa de Haar ocorre na metade direita da figura. Ao juntarmos as transformadas direta e inversa, esperamos que a saída do banco de filtros de dois canais (y[n]) seja exatamente igual à entrada (x[n]), com a possível exceção de um atraso de tempo, tal como y[n] = x[n − k], em que k = 1 para dois coeficientes.

Entrada x[n]

g

g˜ Saída

h

˜h

y[n]

Figura 8.4 Um banco de filtros de dois canais.

Os filtros complementares de um banco de filtros dividem o sinal em subsinais de componentes de baixa frequência e um de componentes de alta frequência. Essa abordagem é denominada codificação de sub-banda. Para a transformada inversa, cada uma das saídas dos filtros (w[n] e z[n]) vai para outro filtro FIR, após o que os dois canais são recombinados por adição para formar y[n]. A ideia é que w[n] e z[n] são uma versão transformada de x[n] e que y[n] é o sinal após a transformada inversa. Esperamos que y[n] seja o mesmo que x[n], considerando-se que não alteramos o sinal ao longo do caminho. Se desejássemos executar uma compressão com perdas, por exemplo, poderíamos alterar z[n] de modo que ele pudesse ser armazenado com mais eficiência. Isso, naturalmente, significaria que y[n] não seria exatamente x[n], mas uma aproximação de x[n]. A discussão aqui presume que desejamos uma reconstrução perfeita, ou seja, que y[n] seja uma cópia exata de x[n], embora possa ser uma versão atrasada no tempo. Para obtermos uma reconstrução perfeita, precisamos escolher nossos coeficientes de filtro com cuidado. Vamos utilizar os seguintes valores:

Weeks 008.indd 231

02/05/12 16:40

232

CAPÍTULO 8

Vejamos o que seriam os sinais w[n] e z[n]. Esta é a transformada direta:

Também precisaremos conhecer w[n − 1] e z[n − 1], que podem ser encontrados substituindo-se n por n − 1:

Agora podemos dizer o que é y[n], após a transformada inversa:

Entretanto, é interessante ver como y[n] relaciona-se com o sinal original, x[n]:

Ao efetuarmos a multiplicação, obtemos o seguinte:

Repetimos a equação anterior, mas eliminando as partes que se cancelam:

Deste modo, terminamos com esta equação compacta que representa o que esse banco de filtros de dois canais faz:

A transformada de Haar é uma transformada de wavelet muito simples. Se escolhermos cuidadosamente nossos coeficientes anteriores a e b, teremos a transformada de Haar. Se 2aa + 2bb = 1, então y[n] = x[n − 1], o que significa que a saída é igual à entrada, apenas atrasada em 1. Podemos lidar com os coeficientes a e b de duas maneiras: 1) podemos tornar ambos iguais a 1, o que facilita a implementação dos filtros, e lembrar em seguida de dividir todos os valores y[n] por (2 × 12 + 2 × 12) = 4; ou 2) podemos escolher nossos valores a e b para nos certificarmos de que (aa + bb) = 1. Se forçarmos aa = 1/2 e bb = 1/2, então a = b = Esses valores correspondem à transformada de Haar. A razão pela qual desejamos que (aa + bb) = 1, e não 2(aa + bb) = 1, tem a ver com os subamostradores, que serão discutidos mais tarde neste capítulo.

8.2

Filtros Espelhados em Quadratura e Filtros em Quadratura Conjugada

Às vezes os bancos de filtros de dois canais são aludidos como codificadores de sub-banda. Aqui, empregaremos os termos de forma intercambiável, embora os codificadores de sub-banda possam ter mais de dois canais. Você também pode encontrar um banco de filtros de dois canais chamado de filtro espelhado em quadratura (QMF, de quadrature mirror filter) ou um filtro em quadratura conjugada (CQF, de conjugate quadrature filter), embora banco de filtros de dois canais seja o mais geral desses três termos. Tanto o QMF quanto o CQF impõem condições aos coeficientes de filtro para cancelar termos de sobreposição espectral e obter uma reconstrução perfeita. Em outras palavras, um filtro espelhado em quadratura e

Weeks 008.indd 232

02/05/12 16:40

A Transformada Discreta de Fourier

233

um filtro em quadratura conjugada são ambos tipos de bancos de filtros de dois canais, com a mesma estrutura. As únicas diferenças entre um QMF, um CQF e outro banco de filtros de dois canais são os coeficientes de filtro. Especificamos os coeficientes em termos de um dos filtros de análise e rotularemos esse conjunto de ˜ g) ˜ normalmente são coeficientes como h, conforme a Figura 8.4. Os outros coeficientes de filtro (g, h, obtidos a partir de h. Afinal de contas, não podemos simplesmente utilizar quaisquer valores que desejemos para os coeficientes de filtro, sob pena de o resultado final não corresponder ao nosso sinal original. Basicamente, um filtro espelhado em quadratura especifica que g utiliza os mesmos coeficientes de filtro que h, mas negativa qualquer outro valor. O filtro de reconstrução h˜ é o mesmo que h, e os coefi˜ Para cientes de filtro g˜ = −g, o que significa que copiamos g para g˜ e negativamos todos os valores de g. um exemplo que utiliza dois coeficientes, examine a Figura 8.5. Uma observação interessante sobre o QMF é que em geral isso não funciona. Em termos práticos, o QMF possui somente dois coeficientes (a menos que se utilizem filtros IIR).

a, –b

Entrada

–a, b Saída

x[n]

Figura 8.5

z[n]

a, b

w[n]

a, b

y[n]

Um filtro espelhado em quadratura para a transformada de Haar.

O filtro em quadratura conjugada especifica g como uma versão invertida de h, com todos os demais valores negativados [36]. Se h = [a, b, c, d], então g será [d, −c, b, −a].1 Para a reconstrução, h˜ e g˜ são versões invertidas de h e g, respectivamente. Se utilizarmos os valores h acima, obteremos h˜ = [d, c, b, a] e g˜ = [−a, b, −c, d]. A Figura 8.6 mostra como isso fica para dois coeficientes.

b, –a

Entrada

–a, b Saída

x[n]

Figura 8.6

z[n]

a, b

w[n]

b, a

y[n]

Um filtro em quadratura conjugada para a transformada de Haar.

Para dois coeficientes iguais (a = b nas figuras), o QMF e o CQF possuem coeficientes de filtro idênticos. O CQF na prática constitui um aperfeiçoamento do QMF, no sentido de que ele funciona para filtros maiores (considerando-se um número par de coeficientes de filtro). Note que as duas figuras, 8.5 e 8.6, especificam a transformada de Haar quando a = b. Utilizaremos essa transformada com frequência, uma vez que ela possui somente dois coeficientes, mas se trata de um exemplo de uma wavelet. Embora existam quatro filtros, o nome (quadratura) na verdade advém do fato de as respostas de frequência dos filtros passa-baixas e passa-altas serem simétricas em torno do ponto de quadratura ␲/2. Considere o espectro da resposta de um filtro: para sinais reais, mostramos somente a primeira metade – as frequências de 0 a ␲. Se dividirmos isso em uma seção ideal passa-baixas e passa-altas, faremos nova divisão até a metade do caminho, ou seja, no ponto ␲/2. O termo espelhado advém do fato de a resposta de magnitude em frequência do filtro passa-altas ser uma imagem espelhada da magnitude em frequência do filtro passa-baixas.

1

Weeks 008.indd 233

O MATLAB utiliza g = [−d, c, −b, a].

02/05/12 16:40

234

CAPÍTULO 8

8.3

Como a Transformada de Haar Equivale a uma Rotação de 45°

Uma base é uma projeção sobre um eixo de coordenadas. Se tivermos um ponto em um gráfico, poderemos falar de sua distância a partir da origem, outro ponto utilizado para referência. Mas isso está restrito somente a um círculo, e portanto necessitamos de mais informações: o ângulo entre o vetor criado da origem até o ponto de interesse e outra linha para referência (ou seja, o eixo x). Para um ponto no espaço bidimensional, essas coordenadas polares constituem informação suficiente para a localização inequívoca do ponto em relação à origem. Contudo, frequentemente utilizamos a base cartesiana {1, 0} e {0, 1} para representar esse ponto, o que nos informa a respeito do ponto em termos de um vetor unitário ao longo do eixo x e outro vetor unitário ao longo do eixo y (eles são normalizados). Sabemos que o eixo y intercepta o eixo x na origem e que há um ângulo de 90° entre eles (eles são ortogonais). De qualquer forma, falando em termos gerais, poderíamos utilizar um conjunto diferente de eixos (ou seja, uma base diferente). A base de Haar corresponde a e em vez da base normal cartesiana. Esta seção responde à pergunta sobre a aparência dos pontos no domínio de Haar em vez do domínio cartesiano. Veremos que os pontos possuem a mesma relação entre eles, mas que o sistema de coordenadas se move numa rotação de 45°. Primeiramente, considere os pontos (x1, y1), (x2, y2) etc. como a lista {x1, y1, x2, y2 etc.}. O sistema cartesiano simplesmente isola os x e os y, como em:

enquanto o sistema Haar faz o seguinte:

(11/

Por exemplo, as coordenadas cartesianas (1, 10) seriam mapeadas para (1 + 10/ , −9/ ) no domínio de Haar.

8.3.1

, 1 − 10/

) ou

Como a Transformada de Haar Afeta o Raio de um Ponto

Considere que esses números sejam vetores (isto é, coordenadas polares). A magnitude e o ângulo podem ser encontrados com um pouco de geometria. Na verdade, a magnitude pode ser encontrada utilizando-se o teorema de Pitágoras, conforme vimos em capítulos anteriores. A magnitude das coordenadas cartesianas (1, 10) é igual a A magnitude das coordenadas de Haar (11/ , −9/ ) é igual a

Esse exemplo mostra que as magnitudes são as mesmas. Em outras palavras, quando consideramos o ponto (1, 10) como a distância a partir da origem e uma rotação a partir do eixo x, a transformada de Haar não afeta a distância. Podemos mostrar que esse é o caso para qualquer ponto arbitrário ao substituirmos (1, 10) por (x, y). No domínio de Haar, teríamos pontos (x + y/ , x − y/ ). Se considerarmos agora esses pontos como coordenadas polares, veremos que a magnitude das coordenadas cartesianas é igual a A magnitude das coordenadas Haar é igual a

Weeks 008.indd 234

02/05/12 16:40

A Transformada Discreta de Fourier

235

Portanto, a transformada de Haar não afeta a distância de um ponto a partir da origem. Contudo, ela realmente afeta o ângulo do ponto em relação ao eixo x.

8.3.2

Como a Transformada de Haar Afeta o Ângulo de um Ponto

Para vermos como o ângulo muda para a transformada de Haar, começaremos novamente com o ponto (1, 10), que, conforme sabemos, torna-se (11/ , −9/ ) no domínio de Haar. Quais são os ângulos que esses pontos criam em relação a seus eixos x? A fórmula para o cálculo desses ângulos (rotulados como theta) é fornecida no Capítulo 1, “Introdução”, e o código abaixo encontra o ângulo dados x e y. Note que atan encontra o arco tangente. if (x > 0) theta = atan (y/x) elseif (x < 0) if (y < 0) theta = atan (y/x) - pi else theta = atan (y/x) + pi end else % O vetor repousa sobre o eixo y. theta = sign(y)*(pi/2) end O ângulo criado pelas coordenadas cartesianas (1, 10) é de 84.29°. O ângulo criado pelas mesmas coordenadas após a transformada de Haar é igual a tan−1 (−9/11) = −39.29°. Se examinarmos a relação entre os dois em geral, ÂnguloCartesianas = tan−1(y/x), enquanto ÂnguloHaar =

A fórmula anterior é um tanto difícil de lidar, devido à natureza ambígua da função arctan e por causa de um problema em potencial de divisão por zero quando x = 0. Para a primeira consideração, isso se torna um pouco mais fácil quando substituímos x e y por seus equivalentes em coordenadas polares. Ou seja, substituiremos x e y por r cos(␪1) e r sin(␪1), respectivamente. Assim, os valores no domínio de Haar são r(cos(␪1) + sin(␪1))/ e r(cos(␪1) − sin(␪1))/ , respectivamente. Ao encontrarmos o novo ângulo (o ângulo no domínio de Haar, ␪2), aplicamos os valores anteriores no domínio de Haar à equação ␪ = tan−1(y/x) para encontrar:

Um aspecto interessante a ser observado é que o termo r/ cancela a si próprio, o que significa que os raios dos valores polares não têm importância. Em outras palavras, o valor para ␪2 depende unicamente de ␪1. Isso era esperado, pois na Seção 8.3.1 vimos que os dois raios são iguais. Portanto, a equação para ␪2 é simplificada para:

Podemos dividir por cos(␪1), o que nos fornece:

Weeks 008.indd 235

02/05/12 16:40

236

CAPÍTULO 8

Em seguida, substituímos sin(␪1)/cos(␪2) por tan(␪1):

Visto que tan(␲/4) = 1, substituiremos o “1” no numerador. Também no denominador, tan(␪1) = 1 × tan(␪1) = tan(␲/4) tan(␪1). Isso nos fornece:

Chegamos ao motivo de toda essa manipulação. Existe uma identidade trigonométrica2 que estabelece:

Não se trata de coincidência o fato de o lado direito da identidade anterior se parecer muito com parte de nossa expressão para ␪2. Uma vez que utilizemos essa identidade, nossa equação será simplificada:

Isso informa aquilo que esperávamos – que a transformada de Haar consiste em uma rotação de ␲/4 radianos, ou 45°.

8.4

Wavelet de Quatro Coeficientes de Daubechies

Vamos também considerar o caso de quatro coeficientes num filtro em quadratura conjugada. A Figura 8.7 mostra uma estrutura semelhante à da Figura 8.6, exceto pelo fato de seus filtros FIR possuírem quatro taps (utilizam quatro coeficientes). As expressões para w[n] e z[n] podem ser encontradas da mesma forma que na Seção 8.1:

Entrada x[n]

Figura 8.7

2

Weeks 008.indd 236

d, –c, b, –a

z[n]

–a, b, –c, d Saída

a, b, c, d

w[n]

d, c, b, a

y[n]

Um banco de filtros de dois canais com quatro coeficientes.

Nossos agradecimentos ao Sr. Srikanth Tirupathi por ter chamado nossa atenção para isso.

02/05/12 16:40

A Transformada Discreta de Fourier

237

Também precisaremos de w[n − 1], w[n − 2] e assim por diante; portanto é útil defini-los para um inteiro k:

Agora reunimos tudo isso e expressamos y[n] em termos de x[n] somente:

Multiplicamos:

Agora reescrevemos essa equação para alinhar os valores x[n − k] de forma apropriada:

Repetimos a equação anterior, apenas eliminando as partes que se cancelam:

Os termos x[n − 1] e x[n − 5] são incômodos, uma vez que desejamos que cada saída de y(n) dependa somente de uma entrada de x(n). Mas, se por acaso ac for igual a −bd, esses termos cancelarão um ao

Weeks 008.indd 237

02/05/12 16:40

238

CAPÍTULO 8

outro. Assim, faremos disso um requisito. Se ac + bd = 0, então os termos x[n − 1] e x[n − 5] são eliminados, o que nos deixa com:

ou y[n] corresponde a uma versão atrasada de x[n], multiplicada por uma constante. Naturalmente, poderíamos ter um segundo requisito – o de que essa constante fosse igual a 1. Os coeficientes de Daubechies satisfazem ambos os critérios. Os coeficientes passa-baixas de db2 podem ser obtidos a partir do MATLAB com o comando a seguir: [lpf, hpf, ilpf, ihpf] = wfilters('db2'); Os quatro coeficientes passa-baixas são então apresentados na variável lpf.

O cálculo de a × c e b × d revela que esses termos certamente cancelam um ao outro quando os somamos,

Podem ocorrer erros de precisão quando se utilizam wavelets, devido à precisão limitada das máquinas. Os coeficientes anteriores são na prática uma aproximação dos seguintes coeficientes de Daubechies (obtidos a partir de [3]):

Note que a referência fornece estes como os coeficientes do filtro passa-altas, o que significa que os valores b e d anteriores foram negativados. Assim,

Portanto,

Se utilizarmos esses coeficientes, veremos que:

O cálculo da soma aa + bb + cc + dd resulta em:

Era isso que esperávamos – que os valores para a, b, c e d sejam escolhidos cuidadosamente de modo que a saída y[n] seja simplesmente uma versão atrasada da entrada x[n]. Anteriormente, contudo, descobrimos que y[n] = 2(aa + bb + cc + dd) x[n − 3], e assim esses coeficientes significam que y[n] = 2x[n − 3]. Por

Weeks 008.indd 238

02/05/12 16:40

A Transformada Discreta de Fourier

239

que o 2 está aqui? A resposta para isso encontra-se na operação de subamostragem. Ainda não examinamos o efeito da operação de subamostragem, mas ela nos fornece as mesmas saídas – só que a 1/2 da escala. Ou seja, se utilizarmos os coeficientes de Daubechies anteriores, mas com subamostradores e sobreamostradores adequados, obteremos y[n] = x[n − 3]. Essa é a razão por que aa + bb + cc + dd = 1, em vez de 1/2. A Seção 8.5 mostra isso em detalhes.

8.5

Subamostragem e Sobreamostragem

A operação de subamostragem é fácil de imaginar: somente os valores indexados pares são mantidos. Ela comprime o sinal até metade de seu tamanho. A operação inversa, de sobreamostragem, expande o sinal de volta, geralmente inserindo um 0 entre cada par de amostras sucessivas. O efeito de um subamostrador seguido de um sobreamostrador é que cada valor alternado (um sim, outro não) será igual a 0. Suponha, por exemplo, que tenhamos a sequência a0, a1, a2, a3, a4, a5, a6. A subamostragem resulta na sequência a0, a2, a4, a6. A sobreamostragem disso nos fornece a0, 0, a2, 0, a4, 0, a6, 0. Neste texto, exibimos o subamostrador como a seta seguida do número 2, como no canto superior esquerdo da Figura 8.8, e o sobreamostrador como no canto superior direito da mesma figura. Como alternativa, alguns textos utilizam os dois símbolos ao longo da base dessa figura [37], onde duas entradas adentram, mas somente uma deixa o subamostrador (2 앖 1), e uma entrada adentra mas duas deixam o sobreamostrador (1 앗 2).

2

2

Figura 8.8

8.5.1

2

1

1

2

Modos diferentes de indicar subamostradores e sobreamostradores.

Exemplo Utilizando Sub/Sobreamostradores

Intuitivamente, pode-se ter a impressão de que a utilização de sobre/subamostradores não pode funcionar. Como podemos descartar metade de nossos cálculos após a filtragem sem perder informação? Esta seção fornece um breve exemplo de como esse processo efetivamente funciona. A Figura 8.9 exibe um exemplo de um banco de filtros de dois canais com subamostradores seguidos de sobreamostradores. Os filtros são muito simples. Na verdade, eles seriam implementados como na Figura 7.2, que é reduzida à Figura 7.3. Uma vez que o filtro com coeficientes {1, 0} permite que a entrada passe direto para a saída, nós a substituiremos por uma linha simples. Da mesma forma, podemos substituir o filtro com coeficientes {0, 1} por uma unidade de retardo. Com essas modificações em vigor, obtemos o filtro revisado da Figura 8.10.

1, 0

Entrada x[n]

0, 1

Figura 8.9

Weeks 008.indd 239

2

2

0, 1

2

2

1, 0

Saída y[n]

Um banco de filtros simples demonstrando sub/sobreamostragem.

02/05/12 16:40

240

CAPÍTULO 8

Entrada x[n]

D

Figura 8.10

2

2

2

2

D Saída y[n]

Um banco de filtros simples demonstrando sub/sobreamostragem, reduzido.

x[0], 0, x[2], 0, x[4], 0, ... x[0], x[2], x[4], ... x[0], x[1], x[2], x[3], x[4], x[5], ...

0, x[0], 0, x[2], 0, x[4], 0, ... 2

2

D y[n]

D

2

2 0, 0, x[1], 0, x[3], 0, x[5], ...

0, x[0], x[1], x[2], x[3], x[4], x[5], ... 0, x[1], x[3], x[5], ... Figura 8.11

Acompanhamento de sinal da entrada à saída de um banco de filtros simples.

Agora estamos prontos para rastrear uma entrada até a saída, com os valores x[0], x[1], x[2], x[3], x[4], x[5] etc. Tenha em mente que essas entradas estão listadas em ordem da esquerda para a direita, de modo que x[0] entre no banco de filtros primeiro, seguido de x[1], e assim por diante. Ao examinarmos o canal superior da Figura 8.11, vemos os valores de entrada indo para o subamostrador, cuja saída é x[0], x[2], x[4] etc., os valores indexados pares de x. A transformada direta termina aqui para esse canal, mas veremos a contribuição dele para a saída. O sobreamostrador insere zeros entre os valores, para gerar a sequência x[0], 0, x[2], 0, x[4], 0 etc. A unidade de retardo vem em seguida, o que significa que cada entrada para ela aparecerá na saída uma medida de tempo mais tarde. Notamos isso na forma de um zero para sua primeira saída, seguido do sinal que chega até ela. A razão para a presença da unidade de retardo deve ficar evidente ao considerarmos o canal inferior. Portanto, o canal superior contribui com um sinal 0, x[0], 0, x[2], 0, x[4], 0 etc., os valores indexados pares de x com zeros entre os valores. Voltamos agora nossa atenção para o canal inferior da Figura 8.11. A unidade de retardo desloca nossas entradas em uma unidade, de modo que sua primeira saída é zero, seguida de x[0], x[1], x[2] etc. Quando esse sinal alcança o subamostrador, este tem o efeito de remover valores alternados. Como 0 é a primeira entrada, ele passa direto, enquanto x[0] é descartado. Assim, a saída do subamostrador é 0, x[1], x[3], x[5] etc. Ao final da transformada direta para o canal inferior, constatamos que temos os valores ímpares para x. O lado de síntese desse banco de filtros para o canal inferior simplesmente sobreamostra o sinal. Isso resulta no padrão 0, 0, x[1], 0, x[3], 0, x[5] etc. A combinação do canal superior com o canal inferior por meio da adição resulta em 0 + 0, x[0] + 0, 0 + x[1], x[2] + 0, 0 + x[3], x[4] + 0, 0 + x[5] etc., ou simplesmente 0, x[1], x[2], x[3], x[4], x[5], e assim por diante – claramente uma versão atrasada do sinal de entrada. Essa demonstração deixa claro que a execução de sub/sobreamostragem nos canais de um banco de filtros não é uma ideia tão absurda quanto parece. Na verdade, o banco de filtros da Figura 8.9 decompõe a entrada em seus valores pares e seus valores ímpares, somando-os de volta em seguida. Isso resulta em uma saída igual à entrada, exceto por um atraso.

Weeks 008.indd 240

02/05/12 16:40

A Transformada Discreta de Fourier

8.5.2

241

Subamostragem e Sobreamostragem com Dois Coeficientes

Com as wavelets, a estrutura do banco de filtros incluirá subamostradores e sobreamostradores na configuração mostrada na Figura 8.12. Em cada canal, o sinal passa por diversas mudanças. Cada uma delas foi rotulada para ficarem distintas. No canal superior temos z[n], que é a saída do primeiro filtro FIR, ou de análise. Em seguida, zd[n] corresponde a z[n] após ter sido subamostrado, zu[n] representa o sinal após a sobreamostragem e, por fim, zf[n] nos fornece os valores finais para aquele canal.

Entrada x[n]

b, –a

z[n]

a, b w[n] Figura 8.12

2

2

zd[n]

wd[n]

2

2

zu[n]

–a, b

zf [n] Saída y[n]

b, a wu[n]

wf [n]

Um banco de filtros de dois canais com sub/sobreamostradores.

Os sinais w[n] e z[n] são tais como eram na Seção 8.1:

Os sinais w[n − 1] e z[n − 1] novamente são encontrados substituindo-se n na equação anterior por n − 1:

Ao examinarmos wd[n] e zd[n], precisamos ser cautelosos. Criamos as declarações a seguir para os valores pares de n. Quando n é ímpar, não existe valor para wd[n] ou zd[n]!

Os sinais wu[n] e zu[n] devem ser tratados com a mesma cautela.

Temos valores para wu[n] e zu[n] para valores pares e ímpares de n. Assim, quando utilizamos esses sinais para n e n − 1, um dos índices deve ser par, enquanto o outro é ímpar. Os sinais finais de cada canal são:

Weeks 008.indd 241

02/05/12 16:40

242

CAPÍTULO 8

e

Quando combinarmos wf[n] e zf[n] para formar y[n], ainda teremos que nos preocupar com o fato de o índice ser par ou ímpar.

Essas expressões podem ser expandidas, para representarmos a saída y[n] em termos da entrada original x[n].

Simplificamos:

Eliminamos os termos que cancelam um ao outro e simplificamos ainda mais:

Esta última etapa é importante. Nela vemos que y[n] encontra-se em termos de x[n − 1], independentemente de n ser par ou ímpar. Deste modo, podemos escrever a saída simplesmente como:

Ao compararmos esse resultado com o da Seção 8.1, também lidando com coeficientes de Haar, percebemos que a diferença é um fator de 2. Os coeficientes de Haar geralmente são fornecidos como a = 1/ , b = 1/ , de modo que (aa + bb) = 1.

8.5.3

Subamostragem e Sobreamostragem com Daubechies 4

Essa é a razão por que os coeficientes da wavelet de quatro coeficientes de Daubechies, quando utilizados sem o subamostrador e o sobreamostrador, terminaram com y[n] − 2x[n − 3]. Se os subamostradores e sobreamostradores forem incluídos, os dois termos desaparecerão. Isso será mostrado mais tarde. Primeiro, começaremos com a Figura 8.13, um banco de filtros atualizado para quatro coeficientes em cada filtro.

Entrada x[n]

d, –c, b, –a

z[n]

a, b, c, d w[n]

2

2

zd[n]

wd[n]

2

2

zu[n]

–a, b, –c, d

zf [n] Saída y[n]

d, c, b, a wu[n]

wf [n]

Figura 8.13 Um banco de filtros com quatro taps por filtro.

Weeks 008.indd 242

02/05/12 16:40

A Transformada Discreta de Fourier

243

Os sinais w[n] e z[n] são exatamente como eram antes:

Os sinais w[n − k] e z[n − k] novamente são encontrados substituindo-se n por n − k:

Ao examinarmos wd[n] e zd[n], novamente precisamos ser cuidadosos quanto ao fato de n ser par ou ímpar. Quando n é ímpar, não existe valor para wd[n] ou zd[n]!

Os sinais wu[n] e zu[n] devem ser tratados com a mesma cautela.

Temos valores para wu[n] e zu[n] para valores pares e ímpares de n. Assim, quando utilizamos esses sinais para n e n − 1, um dos índices deve ser par, enquanto o outro é ímpar. Da mesma forma, se n é par, também o são n − 2, n − 4 etc. Os sinais finais de cada canal são:

e

Ao verificarmos se o índice n é par ou ímpar, podemos encontrar y[n] = wf[n] + zf[n]:

Essas expressões podem ser expandidas, para representarmos a saída y[n] em termos da entrada original x[n].

Weeks 008.indd 243

02/05/12 16:40

244

CAPÍTULO 8

Simplificamos:

Eliminamos os termos que se cancelam e simplificamos ainda mais:

Nesta etapa final, notamos duas coisas. Em primeiro lugar, as expressões são as mesmas independentemente de n ser para ou ímpar, de modo que agora podemos representá-lo como uma declaração única:

Em segundo lugar, percebemos que temos novamente os termos x[n − 1] e x[n − 5], mas que eles podem ser eliminados caso obriguemos ac a ser igual a −bd. Se considerarmos esse o caso, teremos nossa expressão final para y[n]:

Se compararmos esse resultado com o caso anterior (sem subamostragem), veremos que a diferença é um fator 2. Isso explica por que a soma dos quadrados dos coeficientes de Daubechies, (aa + bb + cc + dd), é igual a 1. Quando esse é o caso, y[n] = x[n − 3], ou y[n] é simplesmente uma cópia de x[n], atrasada em três unidades de tempo. Agora que vimos como um sinal pode ser decomposto em dois canais e estes recombinados com sucesso, voltaremos nossa atenção para como isso pode ser utilizado para decompor um sinal em ondas.

8.6

Decomposição de um Sinal em Ondas

Da mesma forma que a transformada de Fourier decompõe um sinal em senoides, a transformada discreta de wavelet gera “pequenas ondas” a partir de um sinal. Essas ondas podem ser somadas para reconstituir o sinal. A Figura 8.14 mostra a análise de wavelet para três oitavas à esquerda, com a síntese padrão (reconstrução) à direita. LPF (de lowpass filter) significa filtro passa-baixas, enquanto HPF (de highpass filter) significa filtro passa-altas. ILPF (inverse lowpass filter) e IHPF (inverse highpass filter) são os filtros passa-baixas e passa-altas inversos, respectivamente. Embora as operações de subamostragem e sobreamostragem não sejam explicitamente mostradas, elas podem ser incluídas nos filtros sem perda de generalidade. À direita, há uma soma implícita onde duas linhas se encontram. A Figura 8.15 mostra uma forma alternativa de realizar a reconstrução. Embora não seja tão eficiente quanto a da Figura 8.14, ela serve para demonstrar a contribuição de cada canal. No final, temos quatro sinais: três sinais de detalhe e uma aproximação nível 3. Da mesma forma que utilizamos a transformada

Weeks 008.indd 244

02/05/12 16:40

A Transformada Discreta de Fourier

HPF

245

IHPF

LPF

HPF

ILPF

IHPF

LPF

HPF

IHPF

LPF

ILPF

ILPF

Figura 8.14 Análise e reconstrução de wavelet.

HPF

LPF

IHPF

HPF

LPF

IHPF

ILPF

HPF

IHPF

ILPF

ILPF

LPF

ILPF

ILPF

ILPF

Figura 8.15 Reconstrução alternativa de wavelet.

de Fourier para encontrar um conjunto de senoides cuja soma corresponde ao nosso sinal original, a Figura 8.15 mostra como a DWT produz um conjunto de subsinais que se somam para formar uma cópia da entrada. Esta seção se baseia no argumento de que podemos reorganizar as operações na reconstrução, ou seja, que podemos executar a sobreamostragem e a filtragem primeiro e depois efetuar a soma. Sabemos que o ILPF e o IHPF são constituídos de filtros FIR que possuem coeficientes de filtro constantes e que, por essa razão, são lineares. Deste modo, sabemos que não importa se somamos dois sinais e filtramos sua soma ou se filtramos os sinais e somamos os resultados. Mas o que acontecerá se tivermos subamostradores e sobreamostradores na análise e na reconstrução? Especificamente para nossos propósitos aqui, não importa se sobreamostramos dois sinais primeiro e os somamos em seguida em vez de somá-los e depois sobreamostrarmos sua soma? Por uma questão de simplicidade, vamos chamar os dois sinais de x e y e considerar apenas as quatro primeiras amostras de cada um deles. Se somarmos esses sinais, obteremos:

Em seguida, sobreamostraremos esse resultado para obter:

Agora considere o que acontecerá se sobreamostrarmos cada sinal primeiro e somá-los em seguida. Após sobreamostrá-los, teremos:

e

Weeks 008.indd 245

02/05/12 16:40

246

CAPÍTULO 8

Se os somarmos agora, obteremos:

Constatamos que isso corresponde exatamente ao resultado da soma prévia e posterior sobreamostragem. Além disso, o argumento anterior é válido para extensões arbitrárias de x e y. Note também que podemos aplicar recursivamente esse raciocínio e concluir que as reconstruções mostradas nas Figuras 8.14 e 8.15 são equivalentes. Na Figura 8.16 vemos os quatro sinais que, quando somados, reconstituem a função impulso. A Figura 8.17 mostra outras quatro ondas que reconstituem a função impulso quando somadas. A diferença entre as duas é a wavelet utilizada: na Figura 8.16 utilizamos a wavelet de Haar, enquanto na Figura 8.17 utilizamos a wavelet de Daubechies-2 (db2). Ambas as figuras mostram como a DWT representa o sinal de entrada como versões deslocadas e escalonadas da wavelet e funções de escalonamento. A função de wavelet corresponde ao filtro passa-altas, enquanto o filtro passa-baixas implementa a função de escalonamento. Visto que utilizamos uma função impulso, a função de wavelet aparece nos sinais de onda alta, enquanto a função de escalonamento aparece nos sinais de onda baixa (ou nos sinais de onda baixa-baixabaixa no caso dessas duas figuras), nomeadas para os filtros que as produzem. Ou seja, os quatro canais que se somam mostrados na parte direita da Figura 8.15 correspondem às ondas alta, baixa-alta, baixabaixa-alta e baixa-baixa-baixa, de cima para baixo. O fato de esses quatro sinais efetivamente cancelarem uns aos outros pode ser difícil de visualizar na figura. Para tornar as coisas mais fáceis, a Tabela 8.1 mostra os valores reais correspondentes aos traçados mostrados na Figura 8.16. O sinal de impulso utilizado possui 100 valores, todos iguais a zero exceto para um valor 1 no offset 50. Em vez de fornecermos todos os valores na tabela, omitimos os zeros e mostramos somente os valores no intervalo 49 a 56. Como o leitor poderá verificar, todas as colunas dessa tabela resultam numa soma zero, exceto para a segunda coluna. Pertencente ao offset 50, a soma dessa coluna é igual a 1, assim como o sinal de impulso original é igual a 1 no offset 50. A Figura 8.18 mostra a entrada (função impulso) versus a saída. Como era de se esperar, o sinal de saída é igual ao sinal de entrada.

Onda alta (detalhe nível 1) 0.5 0 –0.5

10

20

30

40

50

60

70

80

90

100

80

90

100

80

90

100

90

100

Onda baixa-alta (detalhe nível 2)

0.5 0 –0.5

10

20

30

40

50

60

70

Onda baixa-baixa-alta (detalhe nível 3)

0.2 0 –0.2

10

20

30

40

50

60

70

Onda baixa-baixa-baixa-alta (aproximação nível 3)

0.2 0 –0.2

10

20

30

40

50

60

70

80

Figura 8.16 Função impulso analisada com Haar.

Weeks 008.indd 246

02/05/12 16:40

Onda alta (detalhe nível 1) 0.5 0 –0.5

10

20

30

40

50

60

70

80

90

100

80

90

100

80

90

100

90

100

Onda baixa-alta (detalhe nível 2)

0.5 0 –0.5

10

20

30

40

50

60

70

Onda baixa-baixa-alta (detalhe nível 3)

0.5 0 –0.5

10

20

30

40

50

60

70

Onda baixa-baixa-baixa-alta (aproximação nível 3)

0.5 0 –0.5

10

20

30

Figura 8.17

0.5000 0.2500 0.1250 0.1250

50

60

80

70

Função impulso analisada com Daubechies-2.

Tabela 8.1

−0.5000 0.2500 0.1250 0.1250

40

0 −0.2500 0.1250 0.1250

Função impulso analisada com Haar. 0 −0.2500 0.1250 0.1250

0 0 −0.1250 0.1250

0 0 −0.1250 0.1250

0 0 −0.1250 0.1250

0 0 −0.1250 0.1250

Sinal de entrada

1 0.8 0.6 0.4 0.2 0

10

20

30

40

50

60

70

80

90

100

80

90

100

Sinal reconstruído (soma da figura anterior)

1 0.8 0.6 0.4 0.2 0

10

20

30

40

50

60

70

Figura 8.18 Função impulso (original) e sua reconstrução.

Weeks 008.indd 247

02/05/12 16:40

248

CAPÍTULO 8

Onda alta (detalhe nível 1) 10 0 –10 0

1

2

3

4

5

6

7

8

6

7

8

7

8

7

8

Onda baixa-alta (detalhe nível 2)

1 0 –1 0

1

2

3

4

5

Onda baixa-baixa-alta (detalhe nível 3)

2 0 –2 0

1

2

3

4

5

6

Onda baixa-baixa-baixa-alta (aproximação nível 3)

2 1 0 0 Figura 8.19

1

2

3

4

5

6

Exemplo de função decomposta em três níveis de detalhe e aproximação.

Um exemplo mais típico é visto na Figura 8.19, um sinal escolhido por mostrar claramente as wavelets de Haar nos três primeiros subtraçados. (A lista de exemplo {9, −2, 5, −2, 7, −6, 4, −4} foi utilizada aqui.) O traçado superior mostra quatro repetições da wavelet de Haar, cada qual de uma escala diferente. O segundo subtraçado mostra duas repetições, mas elas são duas vezes mais longas do que aquelas acima dele. No terceiro subtraçado, uma wavelet de Haar é vista, com duas vezes a extensão das wavelets acima dela. Nesses subtraçados, podemos ver claramente a wavelet deslocada (ao longo do eixo x) e escalonada (os subtraçados 2 e 3 são produtos tanto da wavelet quanto da função de escalonamento). O subtraçado final contém a aproximação, claramente o resultado de diversas aplicações da função de escalonamento. Todos os valores no domínio da wavelet para esse sinal em particular são positivos. Isso significa que todas as wavelets são fáceis de identificar. Com um valor de escalonamento negativo, a wavelet de Haar correspondente apareceria rebatida em torno do eixo x. Podemos fazer uma comparação interessante entre a DWT e a DFT para a função impulso. Note como a função impulso resulta em ondas compactas. Quase todos os valores de onda fora do intervalo 40:60 são iguais a zero. A transformada de Fourier, contudo, requer muito mais valores para representar tal sinal. Uma vez que a função impulso está bem localizada no tempo, ela se propaga pelo domínio da frequência. O código a seguir mostra um exemplo de execução do programa de ondas DWT. input = zeros(1,100); input(50) = 1; addwaves3(input,'db2') Se utilizássemos um código semelhante para examinar a transformada de Fourier, teríamos algo como o código a seguir. input = zeros(1,100); input(50) = 1; X = fft(input); half = 1:ceil(length(X)/2);

Weeks 008.indd 248

02/05/12 16:40

A Transformada Discreta de Fourier

249

subplot(2,1,1); plot(half, abs(X(half)), 'b'); title('Transformada de Fourier de funcao impulso (magnitudes)'); subplot(2,1,2); plot(half, angle(X(half)), 'r'); title('Transformada de Fourier de funcao impulso (fase)'); A Figura 8.20 mostra os resultados desse código. Note como todas as magnitudes são iguais a 1 e todos os ângulos de fase são diferentes. Isso significa que temos de utilizar 50 senoides diferentes com 50 ângulos de fase diferentes para codificar a função impulso. Poderíamos armazenar as 50 magnitudes de forma compacta, uma vez que todas são de mesmo valor, mas ainda assim seria muita informação para lembrar. Observe que o número 50 é específico para este exemplo e advém diretamente do fato de o sinal original ser um impulso de valor real de 100 pontos. Se repetíssemos esse experimento com, digamos, 1000 pontos (em que todos, com exceção de um, são iguais a zero), terminaríamos com 500 senoides para rastrear. Para uma comparação justa, examinaremos a DWT da função impulso no espaço da wavelet, ou seja, após a análise (examine a metade esquerda da Figura 8.14). Deste modo, utilizaremos o código a seguir. input = zeros(1,100); input(50) = 1; [LPF, HPF, ILPF, IHPF] = myDB2; % Do the wavelet transform L = downsample(myconv(input, LPF)); H = downsample(myconv(input, HPF)); LL = downsample(myconv(L, LPF)); LH = downsample(myconv(L, HPF)); %[LLL, LLH] = myDWT(LL); LLL = downsample(myconv(LL, LPF)); LLH = downsample(myconv(LL, HPF)); clear L LL subplot(4,1,1); plot(1:length(H), H, 'b'); title('Impulso: valores Passa-Altas da DWT');

Transformada de Fourier de função impulso (magnitudes)

1 1 1 1 1

0

5

10

15

20

25

30

35

40

45

50

45

50

Transformada de Fourier de função impulso (fase) 4 2 0 –2 –4

0

5

10

15

20

25

30

35

40

Figura 8.20 Função impulso no domínio de Fourier.

Weeks 008.indd 249

02/05/12 16:40

250

CAPÍTULO 8

subplot(4,1,2); plot(1:length(LH), LH, 'b'); title('Impulso: valores Baixos-Altos da DWT'); subplot(4,1,3); plot(1:length(LLH), LLH, 'b'); title('Impulso: valores Baixos-Baixos-Altos da DWT'); subplot(4,1,4); plot(1:length(LLL), LLL, 'b'); title('Impulso: valores Baixos-Baixos-Baixos da DWT'); length(H) + length(LH) + length(LLH) + length(LLL) ans = 108 Como podemos ver na Figura 8.21, a maioria dos valores no domínio da wavelet é igual a zero. A partir da soma das extensões, o número total de valores que precisamos manter é aproximadamente o mesmo para a DWT e para a FFT (108 versus 100). Contudo, uma vez que os valores no domínio da wavelet são iguais a zero, podemos armazenar essa informação de forma muito mais compacta. Na verdade, o exame dos valores no domínio da wavelet revela que somente oito são diferentes de zero! A partir dessa constatação podemos concluir que alguns sinais podem ser armazenados com mais eficiência no domínio na wavelet do que no domínio da frequência. Naturalmente, isso depende do sinal: aqueles bem localizados no tempo terão uma representação de wavelet mais eficiente, e aqueles bem localizados na frequência terão uma representação de frequência mais eficiente. Outro exemplo da DWT versus a FFT mostra o extremo oposto. Se permitirmos que a função de entrada seja uma senoide pura e repetirmos a análise anterior, veremos que é fácil representá-la no domínio de Fourier. t=0:0.01:.99; input = cos(2*pi*10*t + pi/4); Após executar a FFT desse sinal, vemos que existe somente uma magnitude diferente de zero, ou no mínimo uma no lado positivo do espectro. Embora a FFT retorne ângulos de fase diferentes de zero,

Impulso: valores Passa-Altas da DWT 0 – 0.2 –0.4 5

10

15

20

25

30

35

40

45

50

Impulso: valores Baixos-Altos da DWT 0 – 0.2 –0.4 5

10

15

20

25

Impulso: valores Baixos-Baixos-Altos da DWT 0 – 0.2 –0.4 2

4

6

8

10

12

14

Impulso: valores Baixos-Baixos-Baixos da DWT

0.5 0 –0.5

2

4

6

8

10

12

14

Figura 8.21 Função impulso no domínio de wavelet.

Weeks 008.indd 250

02/05/12 16:40

A Transformada Discreta de Fourier

251

somente aquele que corresponde à magnitude diferente de zero é significativo. Portanto, temos que lembrar somente de dois valores para reconstruirmos esse sinal a partir do domínio da frequência. Se examinarmos o mesmo sinal no domínio da wavelet, descobriremos que todos os 108 valores são diferentes de zero, de modo que teremos muito mais dados para manter. Isso confirma aquilo de que suspeitávamos anteriormente: que a representação ótima de um sinal depende das características do sinal. Tanto a transformada de Fourier quanto a transformada de wavelet têm seu lugar no processamento de sinais, mas os sinais provavelmente serão representados com mais eficiência por uma transformada do que por outra. O programa a seguir gerou os gráficos de ondas que vimos anteriormente. Nós o chamamos de addwaves3 por motivos óbvios, e o algarismo 3 advém do fato de ele funcionar para três oitavas, como na Figura 8.15. % % Dado um sinal de entrada, decomponha-o com DWT. % Mostre a contribuicao de diversos canais da DWT. % % exemplo: entrada = floor(rand(1,100)*256); % addwaves3(input, 'db2'); % Funciona para 'haar' e 'db2' (Haar como padrao) % function addwaves3(input, wavelet) % Obtenha a wavelet de Daubechies2/coeficientes de escalonamento if (strcmp(wavelet,'db2')) [LPF, HPF, ILPF, IHPF] = myDB2; else [LPF, HPF, ILPF, IHPF] = myHaar; end % Execute a transformada de wavelet L = downsample(myconv(input, LPF)); H = downsample(myconv(input, HPF)); %[LL, LH] = myDWT(L); LL = downsample(myconv(L, LPF)); LH = downsample(myconv(L, HPF)); %[LLL, LLH] = myDWT(LL); LLL = downsample(myconv(LL, LPF)); LLH = downsample(myconv(LL, HPF)); % L e LL nao sao mais necessarios clear L LL % Obtenha as ondas para cada contribuicao dos subsinais % Isso efetivamente calcula a reconstrucao. if (strcmp(wavelet,'db2')) % As funcoes ILow e IHigh sao intrinsecas para db2. wave1 = ILow(ILow(ILow(LLL))); wave2 = ILow(ILow(IHigh(LLH))); wave3 = ILow(IHigh(LH)); wave4 = IHigh(H); else % As funcoes ILowHaar, IHighHaar sao intrinsecas para haar. wave1 = ILowHaar(ILowHaar(ILowHaar(LLL))); wave2 = ILowHaar(ILowHaar(IHighHaar(LLH))); wave3 = ILowHaar(IHighHaar(LH)); wave4 = IHighHaar(H); end

Weeks 008.indd 251

02/05/12 16:40

252

CAPÍTULO 8

% O sinal acima precisa receber amostras nulas (prolongado). % Primeiro, encontre a extensao mais longa. max_len = max(max(length(wave1), length(wave2)), ... max(length(wave3), length(wave4))); % Agora ajuste os sinais, conforme necessario. if (length(wave1) < max_len) wave1(length(wave1)+1:max_len) = 0; end if (length(wave2) < max_len) wave2(length(wave2)+1:max_len) = 0; end if (length(wave3) < max_len) wave3(length(wave3)+1:max_len) = 0; end if (length(wave4) < max_len) wave4(length(wave4)+1:max_len) = 0; end % Some todas as ondas waves = wave1 + wave2 + wave3 + wave4; % Iguale a extensao da entrada a da saida % faca isso truncando a saida output = waves(1:length(input)); disp(sprintf('Erro total entre a entrada e a saida: %10.9f.', ... sum(abs(input - output))));

% Agora trace os resultados. figure(1); subplot(4,1,1); plot(wave4); %plot100to1(wave4); title('Onda Alta (detalhe nivel 1)'); subplot(4,1,2); plot(wave3); %plot100to1(wave3); title('Onda Baixa-Alta (detalhe nivel 2)'); subplot(4,1,3); plot(wave2); %plot100to1(wave2); title('Onda Baixa-Baixa-Alta (detalhe nivel 3)'); subplot(4,1,4); plot(wave1); %plot100to1(wave1); title('Onda Baixa-Baixa-Baixa (aproximacao nivel 3)'); figure(2); subplot(2,1,1); plot(input); %plot100to1(input); title('Sinal de Entrada'); subplot(2,1,2); %plot(1:length(output),output,'b',1:length(input),input,'r'); plot(output); %plot100to1(output); title('Sinal reconstruido (soma de fig previa)');

Weeks 008.indd 252

02/05/12 16:40

A Transformada Discreta de Fourier

253

O programa chama a função embutida plot ou o programa especial plot100to1 para a figura de saída, dependendo de quais linhas estão como comentários. O programa plot100to1 confere às wavelets de Haar a melhor aparência possível, por traçar 100 pontos para cada ponto original, tal como o nome sugere. Isso resulta em um gráfico que apresenta transições mais nítidas, tal como aquele mostrado na Figura 8.16.

8.7

Projeto de Filtro Wavelet – Filtros com Quatro Coeficientes

Nesta seção examinaremos o projeto de um banco de filtros geral com quatro taps por filtro, muito semelhante ao da Seção 8.5. Uma diferença, entretanto, é que utilizaremos a transformada z nesta seção, o que na prática torna a análise um pouco mais fácil. A Figura 8.22 mostra como seria a aparência do banco de filtros. Especificamos os valores a, b, c, d e d, c, b, a para os coeficientes em um canal, sabendo que os coeficientes para o outro canal são uma versão invertida e de sinal trocado em relação a essa. Ou seja, no outro canal teríamos os coeficientes d, −c, b, −a e −a, b, −c, d, respectivamente. Utilizaremos H(z) e G(z) para as funções de transferência dos filtros passa-baixas e passa-altas, res˜ e G(z) ˜ são para os filtros inversos correspondentes. H(z) e H(z) ˜ estão reprepectivamente, enquanto H(z) sentadas no canal inferior da Figura 8.22. Ao examinarmos as respostas do filtro individual, vemos que

Utilizamos a notação P0(z) e P0(−z) para representar o produto de dois filtros sequenciais [3], para o canal superior e para o canal inferior, respectivamente, e obtemos as seguintes equações para um CQF com quatro coeficientes por filtro:

˜ Note que −P0(−z) = G(z)G(z), mas não precisamos calcular isso explicitamente. Em vez disso, simplesmente repetimos a expressão para P0 com −z como o argumento. Como vimos na Seção 7.6, −z elevado a uma potência negativa fornece um valor negativo para uma potência ímpar, mas retorna um valor positivo para uma potência par. G(z)

˜ G(z) –P0(–z)

Entrada

Figura 8.22

Weeks 008.indd 253

Saída a, b, c, d

d, c, b, a

H(z)

˜ H(z)

P0(z)

Projeto de um banco de filtros com quatro taps cada.

02/05/12 16:40

254

CAPÍTULO 8

Ou seja, podemos determinar as respostas dos filtros e verificar como os termos cancelam uns aos outros. Em seguida podemos dividir por dois, o que vem a ser o mesmo efeito gerado pelos sub/sobreamostradores. Sem sub/sobreamostradores:

Com sub/sobreamostradores:

Somos deixados com a saída em termos de três versões atrasadas da entrada: z−1, z−3 e z−5. Duas delas devem ser eliminadas, o que pode ser conseguido definindo-se ac + bd = 0, o que nos deixa com (aa + bb + cc + dd) z−3. Além disso, não queremos que o resultado seja uma versão escalonada da entrada, e portanto aa + bb + cc + dd = 1. Também é desejável que a soma dos coeficientes do filtro passa-altas seja igual a zero [3], ou seja, d − c + b − a = 0, para que isso seja uma transformada de wavelet. A pergunta agora é: podemos encontrar valores para a, b, c e d que satisfaçam estas equações?

Uma possibilidade é a utilização dos coeficientes de filtro da wavelet de Daubechies, pois eles satisfazem tais critérios.

8.8

Bases Ortonormais

As transformadas de wavelet de Haar e Daubechies formam bases ortonormais, o que significa que elas são tanto ortogonais quanto normalizadas. Vimos anteriormente que a normalização significa que a saída de um CQF é uma versão atrasada da entrada, em vez de uma versão escalonada. Podemos mostrar isso como o produto interno do filtro passa-baixas consigo mesmo (ou com o filtro passa-altas consigo mesmo), como em < lpf, lpf >. Dado que os coeficientes de filtro passa-baixas são [a, b, c, d], calculamos o produto interno como a multiplicação do primeiro parâmetro com a transposição do complexo conjugado do segundo. Aqui, ambos os parâmetros possuem o mesmo valor real, e obtemos a2 + b2 + c2 + d2, que deve ser 1 para que a transformada seja normalizada. A ortogonalidade, em duas dimensões, significa simplesmente que os componentes estão em ângulos retos. É óbvio, no sistema de coordenadas cartesianas, que um ponto (x, y) pode ser traçado movendo-se x unidades para a direita (ou para a esquerda, caso x seja negativo) e depois movendo-se y unidades para cima ou para baixo (para cima/para baixo, 90° em relação à direita/esquerda) para encontrar a localização do ponto. As bases para o ponto cartesiano (x, y) são (1, 0) e (0, 1). Se encontrarmos o produto interno, < [1, 0], [0, 1] >, deveremos constatar que os resultados em 0 × 1 + 1 × 0 = 0. Definimos ortogonalidade como o atributo com o qual o produto interno das bases é igual a zero, o que também vale para dimensões superiores. Uma vez que o filtro passa-baixas e o filtro passa-altas da transformada de wavelet formam uma base ortogonal, o sinal pode ser decomposto em partes separadas. Mesmo com a subamostragem, os dois canais contêm informação suficiente para reconstruir o sinal completo, tal como um banco de filtros que decompõe o sinal em componentes pares e ímpares. O programa a seguir demonstra, para diversas wavelets, que os coeficientes de filtro são tanto ortogonais quanto normais. Consideramos que todos os valores de coeficiente são reais e, portanto, não é preciso encontrar os complexos conjugados. (Embora as wavelets complexas efetivamente existam.) % % % %

Weeks 008.indd 254

Mostre que os coeficientes de escalonamento/wavelet formam uma base, ou seja, lpf * hpf.' = 0 e lpf * lpf.' = 1

02/05/12 16:40

A Transformada Discreta de Fourier

255

lpf = [1 1] * (1/sqrt(2)); hpf = [-1 1]* (1/sqrt(2)); disp(' encontrando o produto interno da wavelet/wavelet de Haar'); disp(' deve ser um'); hpf * hpf.' disp(' agora encontre o produto interno de escalonamento/escalonamento de Haar'); disp(' deve ser um, também.'); lpf * lpf.' disp(' agora encontre o produto interno de escalonamento/wavelet de Haar'); disp(' deve ser zero.'); lpf * hpf.' Primeiramente podemos rodar este código para verificar a base de Haar. encontrando o produto interno da wavelet/wavelet de Haar deve ser um ans = 1.0000 agora encontre o produto interno de escalonamento/escalonamento de Haar deve ser um, também. ans = 1.0000 agora encontre o produto interno de escalonamento/wavelet de Haar deve ser zero. ans = 0 Conforme o esperado, isso funciona para a base de Haar. Agora podemos repetir o procedimento para a wavelet de quatro coeficientes de Daubechies. Rotulamos os filtros como lpf2 e hpf2 para mantê-los separados dos coeficientes de filtro passa-baixas e filtro passa-altas que utilizamos anteriormente. a b c d

= = = =

(1-sqrt(3))/(4*sqrt(2)); (3-sqrt(3))/(4*sqrt(2)); (3+sqrt(3))/(4*sqrt(2)); (1+sqrt(3))/(4*sqrt(2));

lpf2 = [a b c d]; hpf2 = [-d c -b a]; ans2a = hpf2 * hpf2.'; disp(sprintf(' wavelet/wavelet de Daubechies = %6.4f', ans2a)); ans2b = lpf2 * lpf2.'; disp(sprintf(' escalonamento/escalonamento de Daubechies = %6.4f', ans2b)); ans2c = lpf2 * hpf2.'; disp(sprintf(' escalonamento/wavelet de Daubechies = %6.4f', ans2c)); A execução desse código produz: wavelet/wavelet de Daubechies = 1.0000 escalonamento/escalonamento de Daubechies = 1.0000 escalonamento/wavelet de Daubechies = 0.0000

Weeks 008.indd 255

02/05/12 16:40

256

CAPÍTULO 8

Portanto, o código anterior confirma a wavelet de quatro coeficientes de Daubechies. O código para repetir esse experimento para diversas outras wavelets vem a seguir. Entretanto, em vez de especificarmos por conta própria os coeficientes de filtro, confiaremos nas funções de wavelet embutidas. try [lpf3, hpf3, lo_recon, hi_recon] = wfilters('db44'); catch disp('Você deve dispor da caixa de ferramentas wavelet para as outras'); break; end

ans3a = hpf3 * disp(sprintf(' ans3b = lpf3 * disp(sprintf(' ans3c = lpf3 * disp(sprintf('

hpf3.'; wavelet/wavelet de Daubechies44 = %6.4f', ans3a)); lpf3.'; escalonamento/escalonamento de Daubechies44 = %6.4f', ans3b)); hpf3.'; escalonamento/wavelet de Daubechies44 = %6.4f', ans3c));

[lpf4, hpf4, lo_recon, hi_recon] = wfilters('coif1'); ans4a = hpf4 * hpf4.'; disp(sprintf(' wavelet/wavelet de Coiflets1 = %6.4f', ans4a)); ans4b = lpf4 * lpf4.'; disp(sprintf(' escalonamento/escalonamento de Coiflets1 = %6.4f', ans4b)); ans4c = lpf4 * hpf4.'; disp(sprintf(' escalonamento/wavelet de Coiflets1 = %6.4f', ans4c)); [lpf5, hpf5, lo_recon, hi_recon] = wfilters('sym8'); ans5a = hpf5 * hpf5.'; disp(sprintf(' wavelet/wavelet de Symlets8 = %6.4f', ans5a)); ans5b = lpf5 * lpf5.'; disp(sprintf(' escalonamento/escalonamento de Symlets8 = %6.4f', ans5b)); ans5c = lpf5 * hpf5.'; disp(sprintf(' escalonamento/wavelet de Symlets8 = %6.4f', ans5c)); [lpf6, hpf6, lo_recon, hi_recon] = wfilters('dmey'); ans6a = hpf6 * hpf6.'; disp(sprintf(' wavelet/wavelet de Meyers Discreto = %6.4f', ans6a)); ans6b = lpf6 * lpf6.'; disp(sprintf(' escalonamento/escalonamento de Meyers Discreto = %6.4f', ans6b)); ans6c = lpf6 * hpf6.'; disp(sprintf(' escalonamento/wavelet de Meyers Discreto = %6.4f', ans6c)); A saída desse programa (utilizando a versão para estudantes do MATLAB para mostrar os comandos try e catch) aparece a seguir. EDU>> innerproducts Você deve dispor da caixa de ferramentas wavelet para as outras EDU>> Uma observação final sobre os produtos internos é que o produto interno de um sinal (vetor) com ele mesmo resulta na extensão do vetor elevada ao quadrado.

Weeks 008.indd 256

02/05/12 16:40

257

A Transformada Discreta de Fourier

Se x = {x0, x1}, sabemos que seu produto interno: < x, x > = x 20 + x 21. Também sabemos que sua extensão é . Ao utilizarmos essas duas informações, é fácil verificar que podemos definir a extensão do vetor em termos de um produto interno, conforme acabamos de ver. O suporte ao sinal x não importa, já que definimos a extensão como a raiz quadrada do produto interno de x com ele mesmo, mesmo quando x possui mais de dois valores. Alguns textos [38] também chamam essa extensão de norma.

8.9

Multirresolução

A discussão anterior diz respeito a um par de filtros de análise. Multirresolução consiste no processo de se tomar a saída de um canal e aplicá-la em outro (ou mais) par(es) de filtros de análise. Para a transformada de wavelet, fazemos isso com a saída do filtro passa-baixas. Os pacotes de wavelet, entretanto, utilizam um par de filtros adicionais para cada canal. Examinaremos a multirresolução começando pela transformada de wavelet de quatro coeficientes de Daubechies, com subamostragem e sobreamostragem, para dois níveis de resolução (oitavas), conforme mostrado na Figura 8.23. Se você comparar essa figura com o código simpleDWTexample.m mostrado no início deste capítulo, as variáveis a1, a2, d1 e d2 do programa correspondem respectivamente aos rótulos wd, w2d, zd e z2d.

d, –c, b, –a

Entrada

z[n]

zd[n]

2

d, –c, b, –a x[n]

a, b, c, d w[n]

2

wd[n]

z2[n]

a, b, c, d w2[n]

zd[n] z2d[n]

2

z2u[n]

–a, b, –c, d

z2f [n] wd[n]

w2d[n]

2

w2u[n]

d, c, b, a

2

2

z2u[n]

2

2

–a, b, –c, d

z2d[n]

w2d[n]

z2f [n] Saída

w2u[n]

y[n]

d, c, b, a w2f [n]

w2f [n] Figura 8.23 Dois níveis de resolução.

Os sinais w[n], wd[n], z[n] e zd[n] são os mesmos de antes.

Para gerar w2[n], w2d[n], z2[n] e z2d[n], primeiramente notamos que eles possuem com wd[n] a mesma relação que wd[n] e zd[n] possuem com x[n]. Em outras palavras, podemos reutilizar as equações anteriores e substituir x[n] por wd[n]. Além disso, como existe um subamostrador entre w2[n] e w2d[n], todo valor alternado (um sim, outro não, sucessivamente) de w2[n] será eliminado. O sinal w2[n], por definição, é baseado em wd[n], uma versão subamostrada de w[n]. A utilização do n original, portanto, significa que devemos dizer que todo valor alternado a partir dos valores pares de n será eliminado. Em outras palavras, os únicos valores que passarão pelo segundo subamostrador são aqueles em que n é uniformemente divisível por 4.

Weeks 008.indd 257

02/05/12 16:40

258

CAPÍTULO 8

Ao examinarmos agora a parte da reconstrução (síntese), w2u[n] e z2u[n] são versões sobreamostradas de w2d[n] e z2d[n]. Precisamos monitorar os valores de n que são divisíveis por 4 e também os valores de n que são pares, mas não divisíveis por 4, como o número 6. Uma vez que tomamos um sinal em que todos os termos do índice já são pares, a função do subamostrador seguido do sobreamostrador é manter os índices par-par (divisíveis por 4) e eliminar os índices ímpar-par (divisíveis por 2, mas não divisíveis por 4, tais como 2, 6, 10 etc.), zerados em seguida pelo sobreamostrador.

Os sinais w2f[n] e z2f[n] são encontrados da mesma maneira de antes:

Podemos concluir a reconstrução para a segunda oitava examinando o resultado quando w2f[n] e z2f[n] são somados para recriar wd[n]. Chamaremos esse sinal recriado de wd[n]⬘, até termos certeza de que ele é igual ao wd[n].

O importante aqui é mostrar que os dois sinais marcados com wd[n] na Figura 8.23 são exatamente os mesmos. Isso significa encontrar a expressão anterior em termos de w[n], exclusivamente. Primeiramente, tomaremos as expressões para w2[n] e z2[n] e encontraremos w2[n − k] e z2[n − k].

Weeks 008.indd 258

02/05/12 16:40

A Transformada Discreta de Fourier     259





Agora podemos aplicá-las nas expressões wd[n] anteriores.





Ao reescrevemos essas expressões e as alinharmos em colunas, n é par-par:





n é ímpar-par:





Ao cancelarmos nossos termos, obtemos: n é par-par:





n é ímpar-par:





Ao examinarmos as equações anteriores, constatamos que temos a mesma expressão, seja quando n é ímpar-par, seja quando é par-par. Assim, podemos reescrever a expressão anterior, com a premissa de que n deve ser par. Vimos na seção anterior que ac = −bd, de modo que os termos com wd[n − 1] e wd[n − 5] se cancelarão. Isso nos deixa com wd[n]9 = (aa + bb + cc + dd) wd[n − 3]. Conforme observado ante­ riormente, (aa + bb + cc + dd) = 1, e portanto obtemos a expressão final para wd[n]9 = wd[n − 3]. O wd[n]9 reconstruído é igual à versão atrasada do original wd[n]. Isso não surpreende, pois simplesmente inserimos

Weeks 008.indd 259

07/05/12 14:37

260

CAPÍTULO 8

uma cópia da estrutura do banco de filtros da primeira oitava entre o original e a reconstrução. Quando examinamos a estrutura do banco de filtros da primeira oitava, vimos que o resultado y[n] era uma versão atrasada de x[n]. A segunda etapa consiste em tomar o sinal reconstruído, wd[n], e recombiná-lo com zd[n] para se obter y[n]. Note que wd[n] na análise (parte superior) da Figura 8.23 e wd[n]⬘ na síntese (parte inferior) dessa figura devem ser exatamente iguais. Porém, vimos anteriormente que isso não acontece, pois wd[n]⬘ é uma versão atrasada de wd[n]. Podemos lidar com isso de duas maneiras. Se estamos em um projeto de hardware que executa a transformada de wavelet, podemos simplesmente acrescentar três (ou seja, a extensão do filtro − 1) unidades de retardo ao canal com o sinal zd[n]. Se estamos criando um software para fazer isso, podemos acrescentar três zeros antes de zd[n] ou deslocar wd[n]⬘ para frente três posições, descartando os três primeiros valores. O importante é que wd[n]⬘ e zd[n] estejam apropriadamente alinhados. Consideraremos que esse segundo método tem sido empregado para corrigir wd[n]⬘, para que seja exatamente igual a wd[n]. Se tivéssemos que utilizar o primeiro método de atraso de zd[n], a saída seria reconstruída utilizando-se wd[n − 3] e zd[n − 3], implicando que a saída seria atrasada por três unidades adicionais. Ou seja, em vez de termos y[n] = x[n − 3] como resultado, teríamos y[n] = x[n − 3 − 3] = x[n − 6]. O rastreamento do percurso de wd[n] e zd[n] é muito semelhante ao de w2d[n] e z2d[n].

Observe que a condição “de outra forma” foi eliminada, já que n ou é par ou é ímpar. Encontrar wf[n] e zf[n] é exatamente o mesmo que encontrar w2f[n] e z2f[n]:

A soma de wf[n] a zf[n] produz y[n], da mesma forma que vimos no caso da primeira oitava. O aspecto importante a se observar é que a reconstrução é exatamente a mesma que a do caso da primeira oitava, desde que wd[n] seja exatamente o mesmo em ambas as partes (superior e inferior) de análise e síntese da Figura 8.23. Vimos aqui que podemos obrigar a estrutura do banco de filtros a trabalhar de forma recursiva e ainda assim obter um sinal perfeitamente reconstruído. Embora esta seção utilize apenas dois níveis de recursão (oitavas), deve ficar claro que podemos utilizar quantas oitavas desejarmos. A única limitação é aquela imposta pela extensão do sinal de entrada, pois cada oitava utiliza metade do número de entradas como a oitava acima dela. Em outras palavras, wd[n] existe somente para valores pares do índice n, enquanto o sinal de entrada x[n] possui valores para valores pares e ímpares de n. Se x[n] possuísse 16 amostras, wd[n] teria 8, w2d[n] teria 4 e assim por diante.

8.10

Wavelets Biortogonais

Quando o assunto é wavelets, a transformada é classificada como ortogonal ou biortogonal. As transformadas de wavelet discutidas anteriormente (Haar e Daubechies) são ambas ortogonais. Elas também podem ser normalizadas, tal como a utilização de 1/ e −1/ em vez de 1/2 e −1/2 para os coeficientes

Weeks 008.indd 260

02/05/12 16:40

A Transformada Discreta de Fourier

Entrada x[n]

d, –f, d

z[n]

–a, b, –c, b,–a

261

zf [n] Saída

a, b, c, b, a

w[n]

d, f, d

wf [n]

y[n]

Figura 8.24 Transformada de wavelet biortogonal.

de Haar, durante o uso de sub/sobreamostradores. Se considerar que simplesmente extraímos a raiz quadrada de 1/2, você terá deixado passar uma etapa. Em vez disso, multiplique por , ou seja, (1/2) = /2 = 1/ . Quando a transformada é ortogonal e normal, nós a chamamos de ortonormal. A Figura 8.24 exibe um exemplo de transformada de wavelet biortogonal e sua inversa para uma única oitava. Procedemos como antes, começando pelas definições para w[n] e z[n]:

Agora podemos definir wf[n] e zf[n] em termos de w[n] e z[n]:

Em seguida, substituímos os z[n] e w[n] por suas definições:

E elas são somadas para que obtenhamos y[n], a saída:

A remoção dos termos que se cancelam e a simplificação nos fornecem:

Para fazermos de y[n] uma versão atrasada de x[n], precisamos que a expressão anterior esteja em termos de um único índice para x, ou seja, x[n − 3]. Isso pode ser obtido se definirmos 2(bd + af) = 0, ou bd = −af. Outra condição é que não queremos ter de dividir a saída por uma constante. Nesse caso, isso quer dizer que o tempo (4bd + 2cf) deve ser igual a 1. Entretanto, estamos propensos a utilizar essa transformada com subamostradores e sobreamostradores, o que significa que desejaríamos que o termo (2bd + cf) fosse igual a 1.

Weeks 008.indd 261

02/05/12 16:40

262

CAPÍTULO 8

Entrada x[n]

d, –f, d

a, b, c, b, a

z[n]

w[n]

2

2

zd[n]

wd[n]

2

2

zu[n]

–a, b, –c, b,–a

zf [n] Saída

wu[n]

d, f, d

wf [n]

y[n]

Figura 8.25 Transformada de wavelet biortogonal.

Aqui, repetimos a análise anterior com a complexidade adicional de subamostradores e sobreamostradores. A Figura 8.25 mostra esse caso. As definições para w[n] e z[n] são as mesmas exibidas anteriormente. Os sinais após os subamostradores são wd[n] e zd[n]3*. Após os sobreamostradores, os sinais são rotulados como wu[n] e zu[n]*.

Em seguida, substituímos os zu[n] e wu[n] por suas definições:

Multiplicamos:

* d vem down-sampler, e u vem de up-sampler. (N.T.)

Weeks 008.indd 262

02/05/12 16:40

A Transformada Discreta de Fourier

263

Simplificamos:

Após combiná-los e removermos os termos que se cancelam, obtemos:

Seja n par ou ímpar,

Vemos que se trata da mesma equação usada para wavelets biortogonais sem sub/sobreamostradores, só que sem um fator 2 por toda parte. Isso novamente confirma a noção de que os sub/sobreamostradores removem dados redundantes, tornando a transformada eficiente. Embora não tenham sido fornecidos valores para a, b, c, d e f, mostramos as condições necessárias para que tais valores trabalhem como uma transformada de wavelet biortogonal. O MATLAB provê diversas wavelets biortogonais, e bior2.2 corresponde ao padrão na Figura 8.24. Isso é demonstrado na próxima seção. >> [lpf, hpf, ilpf, ihpf] = wfilters('bior2.2'); >> lpf lpf = Columns 1 through 4 0

-0.1768

0.3536

1.0607

-0.7071

0.3536

Columns 5 through 6 0.3536

-0.1768

>> hpf hpf = Columns 1 through 4 0

Weeks 008.indd 263

0.3536

02/05/12 16:40

264

CAPÍTULO 8

Columns 5 through 6 0

0

Embora seis valores tenham sido retornados na lista lpf (para filtro passa-baixas, ou lowpass filter), um deles é zero. Os outros cinco claramente possuem o padrão a, b, c, b, a. Para o filtro passa-altas, temos um valor médio f com o mesmo valor d em qualquer lado dele. Deste modo, esses coeficientes fornecem um exemplo real de uma wavelet biortogonal.

8.11

Teoria da Transformada de Wavelet

A transformada discreta de wavelet convolve a entrada por meio dos deslocamentos (translações no tempo) e escalonamentos (dilatações e contrações) da wavelet. As variáveis frequentemente utilizadas na literatura da wavelet são as seguintes: g representa o filtro passa-altas (wavelet) h representa o filtro passa-baixas (escalonamento) J é o número total de oitavas j é a oitava corrente (utilizada como um índice. 1 < = j < = J) N é o número total de entradas n é a entrada corrente (utilizada como um índice. 1 < = n < = N) L é a extensão do filtro (número de taps) k é o coeficiente de wavelet corrente Wf (a, b) representa a transformada contínua de wavelet (CWT) da função f Wh [j, n] representa a transformada discreta de wavelet da função f W [j, n] representa a transformada discreta de escalonamento da função f, exceto: W [0, n], que é o sinal de entrada. A transformada contínua de wavelet é representada por [2]:

em que f(t) é a função a ser analisada, ␺ é a wavelet e ␺((t − b)/a) é a versão deslocada e escalonada da wavelet no tempo b e na escala a. Uma forma alternativa da equação utiliza s e u no lugar de a e b:

novamente em que ␺ é a wavelet, deslocada por u e escalonada por s. Embora efetivamente existam wavelets complexas, utilizamos somente aquelas com valores reais neste texto. Assim, as equações nem sempre especificam o complexo conjugado da wavelet. Podemos reescrever a transformada de wavelet como um produto interno [1]:

Este produto interno é essencialmente computado pelos filtros. Até aqui, essa fundamentação teórica esteve voltada para a obtenção da transformada de wavelet tendo sido fornecida uma wavelet, mas como obter os coeficientes de wavelet e implementar a transformada com filtros? A relação entre wavelets e os bancos de filtros que implementam a transformada de wavelet é examinada a seguir. A função de escalonamento, ␾(t), é determinada por meio da aplicação recursiva dos coeficientes de filtro, visto que a multirresolução convolve recursivamente o vetor de entrada após o deslocamento e o escalonamento. Todas as informações a respeito das funções de escalonamento e

Weeks 008.indd 264

02/05/12 16:40

A Transformada Discreta de Fourier

265

de wavelet podem ser encontradas pelos coeficientes da função de escalonamento e da função de wavelet, respectivamente [3]. A função de escalonamento é dada como:

A função de wavelet é dada como:

Existe um conjunto finito de coeficientes h[k]. Uma vez que esses coeficientes tenham sido encontrados, permitindo-nos projetar o filtro passa-baixas, os coeficientes de filtro passa-altas são fáceis de encontrar. Daubechies [2] calculou os coeficientes a seguir, com muitas propriedades desejáveis (e necessárias), que satisfazem as equações anteriores. Coeficientes passa-baixas (escalonamento) [3]:

Eles produzem um espaço, Vj, que apresenta invariância para deslocar e escalonar, o que é necessário para a análise de multirresolução. Agora que dispomos de coeficientes para o filtro passa-baixas, os coeficientes de filtro passa-altas (wavelet) correspondentes podem ser calculados da seguinte maneira:

Note que g[0] = h[3], g[1] = −h[2], g[2] = h[1] e g[3] = −h[0]. Esse padrão aparece no banco de filtros mostrado na Figura 8.5. A importância dos coeficientes anteriores é que eles são utilizados em um banco de filtros para gerar a transformada de wavelet [3]. Vamos escrever a equação da wavelet em relação à sequência x[n] amostrada discretamente [39]:

onde a wavelet é substituída pela função g, que é obtida amostrando-se a função wavelet contínua. Para o caso discreto, consideramos a = 2k e exigimos que tanto os parâmetros (a, b) quanto k sejam inteiros. Para tornar isso mais claro, substituímos a e b (que representam números reais acima) por j e n (que representam números inteiros). Com o caso discreto, a redundância não é necessária no sinal transformado. Para reduzir a redundância, a equação de wavelet deve satisfazer determinadas condições. Em primeiro lugar, devemos introduzir uma função de escalonamento ortogonal, o que nos permite obter uma aproximação no ultimo nível de resolução (o nível inicial de resolução é simplesmente o sinal de entrada). Como vimos em seções anteriores, os coeficientes para o filtro de escalonamento (passa-baixas) e o filtro de wavelet (passa-altas) são intimamente relacionados. As funções que produzem estes coeficientes também são dependentes uma da outra. Em segundo lugar, a representação do sinal na oitava j deve conter toda a informação do sinal na oitava j + 1, o que faz sentido: o sinal de entrada possui mais informação que a primeira aproximação deste sinal. A função x[n] é, portanto, alterada para W [j − 1, m], que vem a ser a decomposição da função de escalonamento a partir do nível anterior de resolução, com m como um índice. Em terceiro lugar, após J níveis de resolução, o resultado da função de escalonamento no sinal será igual a 0. Após visualizar repetidamente um sinal em aproximações cada vez menos refinadas, eventualmente a função de escalonamento não produzirá qualquer informação útil. Em quarto lugar, a função de escalonamento nos permite aproximar qualquer sinal fornecido com um nível de precisão variável [8]. A função de escalonamento, h, nos fornece uma aproximação do sinal por meio da equação a seguir. Isso também é conhecido como a saída passa-baixas:

Weeks 008.indd 265

02/05/12 16:40

266

CAPÍTULO 8

A função wavelet nos fornece o sinal de detalhe, também chamado de saída passa-altas:

O termo n nos fornece o deslocamento, os pontos de partida para os cálculos da wavelet. O índice 2n − m incorpora o escalonamento, resultando em metade das saídas para a oitava j comparada à oitava anterior, j − 1.

Exemplo 8.1 Suponha que g = {1/ , −1/ } e x = {34, 28, 76, 51, 19, 12, 25, 43}. Encontre o sinal de detalhe produzido pela transformada de wavelet neste sinal. Resposta:

Para este exemplo, o valor de g com qualquer valor de índice fora de [0, 1] é zero. Além disso, o valor de W [j, n] = 0 para qualquer valor n inferior a 0 ou maior ou igual a N. Deste modo, podemos encontrar os valores individuais para Wh da seguinte maneira: Exame da primeira oitava:

Weeks 008.indd 266

02/05/12 16:40

A Transformada Discreta de Fourier

x[n]

Figura 8.26

a, b

y[n]

267

Saída 2

Um canal do lado da análise de um banco de filtros.

Deve ser óbvio que qualquer valor Wh [1, n] = 0 quando n > − 5. Ao compararmos isso à abordagem do banco de filtros, consideramos a Figura 8.26, que mostra um canal de um banco de filtros. Tendo em mente que g[0] = a e g[1] = b, podemos rastrear o sinal da seguinte maneira:

A saída é uma versão subamostrada de y. Portanto, ao eliminarmos cada valor alternado (um sim, um não), obtemos:

Terminamos com o mesmo resultado, conforme o esperado. Naturalmente, a equação de escalonamento é a mesma equação básica, com coeficientes diferentes. Antes que possamos examinar a segunda oitava, precisamos conhecer os valores W [1, n], que apresentamos da maneira a seguir. Perceba que estes podem ser encontrados da mesma forma que acabamos de mostrar, exceto pelo fato de ambos os coeficientes de filtro serem positivos.

Exame da segunda oitava:

Weeks 008.indd 267

02/05/12 16:40

268

CAPÍTULO 8

Novamente, podemos verificar isso com a abordagem do banco de filtros. Note como o termo g[2n − m] encarrega-se da subamostragem para nós. Podemos prosseguir com a terceira oitava. Primeiramente, precisamos das saídas passa-baixas da segunda oitava, encontradas da mesma forma que acabamos de mostrar, apenas com coeficientes diferentes.

Exame da terceira oitava:

Weeks 008.indd 268

02/05/12 16:40

A Transformada Discreta de Fourier

269

Encontramos as saídas passa-baixas para esta oitava:

Vamos parar por aqui, pois não obtemos mais informações úteis após este ponto. Por quê? Porque nosso sinal original possui apenas oito amostras e cada oitava utiliza somente metade dos dados da oitava anterior. Em outras palavras, uma vez que dispomos de 23 valores, não devemos executar mais do que três oitavas de resolução no sinal.

Exemplo 8.2 Podemos verificar os resultados do exemplo anterior em um computador? Resposta: Naturalmente podemos verificá-los com um computador. Para tal, precisamos de duas ferramentas: convolução e subamostragem. Existem formas mais eficientes, mas esta certamente é fácil. O MATLAB disponibiliza um programa de convolução, conv. Ou poderíamos utilizar o programa myconv, disponível no site da LTC Editora. O comando a seguir gera uma lista chamada output (saída) que armazena uma versão subamostrada da lista input (entrada). output = input(2:2:length(input)); Temos, contudo, que lidar com um pequeno problema. As listas do MATLAB começam pelo índice um e não admitem um índice zero. A notação matemática começa pelo índice zero, de modo que existe uma incompatibilidade entre as duas. Sempre podemos adicionar um ao índice para fazer isso funcionar, mas isso significa que nossa função de subamostragem deve manter os valores de índice ímpar fora da lista input. Utilizaremos uma função chamada downsample_odd para este propósito. Eis o programa, em sua totalidade: function [output] = downsample_odd(input) output = input(1:2:length(input)); Com uma função para subamostragem e uma embutida para convolução, estamos prontos para calcular este exemplo. Primeiramente, definimos x, seguido de nossas funções de wavelet e escalonamento (g e h, respectivamente). Em seguida, convolvemos o sinal de exemplo com a função de escalonamento para produzir L, a saída passa-baixas, e subamostramos o resultado. Da mesma forma, encontramos a saída passa-altas H. Estas correspondem à decomposição da primeira oitava. Em seguida, repetimos a convolução e a subamostragem, utilizando L como nosso sinal para encontrar as saídas passa-baixas-baixas (LL) e as saídas passa-baixas-altas (LH), formando a segunda oitava. Por fim, uma passada adicional gera LLL e LLH a partir do resultado LL, fornecendo-nos a terceira oitava. x = [34, 28, 76, 51, 19, 12, 25, 43]; g = [1/sqrt(2), -1/sqrt(2)]; h = [1/sqrt(2), 1/sqrt(2)]; % % L H

Weeks 008.indd 269

oitava 1 convolva e depois subamostre = downsample_odd(conv(x, h)); = downsample_odd(conv(x, g));

02/05/12 16:40

270

CAPÍTULO 8

% oitava 2 LL = downsample_odd(conv(L, h)); LH = downsample_odd(conv(L, g)); % oitava 3 LLL = downsample_odd(conv(LL, h)); LLH = downsample_odd(conv(LL, g)); Após executarmos estes comandos, precisamos agora comparar os resultados com os cálculos que encontramos. Copiamos as respostas que acabamos de encontrar, alterando a notação para conformá-la ao modo que o computador entende. Agrupamos os termos Wh e W. W_h = [ 34/sqrt(2), 48/sqrt(2), -32/sqrt(2), ... 13/sqrt(2), -43/sqrt(2) ]; W_h(2, :) = [ 34/2, -34/2, 6/2, 0, 0 ]; W_h(3, :) = [ 17/sqrt(2), -47/sqrt(2), 0, 0, 0 ]; W = [ 34/sqrt(2), 104/sqrt(2), 70/sqrt(2), ... 37/sqrt(2), 43/sqrt(2) ]; W(2, :) = [ 34/2, 174/2, 80/2, 0, 0 ]; W(3, :) = [ 17/sqrt(2), 127/sqrt(2), 0, 0, 0 ]; Agora que o computador possui os cálculos e os resultados da convolução e da subamostragem, podemos comparar os dois. Exibimos nossos resultados calculados para W e os comparamos visualmente às listas L, LL e LLL. >> disp(W) 24.0416 17.0000 12.0208

73.5391 87.0000 89.8026

49.4975 40.0000 0

26.1630 0 0

30.4056 0 0

>> disp(L) 24.0416

73.5391

49.4975

26.1630

30.4056

>> disp(LL) 17.0000

87.0000

40.0000

>> disp(LLL) 12.0208 89.8026 A partir disso, constatamos que os valores coincidem. Em seguida, comparamos Wh à H, LH e LLH. >> disp(W_h) 24.0416 33.9411 17.0000 -17.0000 12.0208 -33.2340

-22.6274 3.0000 0

9.1924 0 0

-30.4056 0 0

>> disp(H) 24.0416

33.9411

-22.6274

9.1924

-30.4056

>> disp(LH) 17.0000 -17.0000

3.0000

>> disp(LLH) 12.0208 -33.2340

Weeks 008.indd 270

02/05/12 16:40

A Transformada Discreta de Fourier

271

Novamente, confirmamos visualmente que os valores calculados destas duas maneiras coincidem. Para reconstruirmos o sinal, precisamos de LLL, a aproximação da terceira oitava, juntamente com os detalhes LLH, LH e H, correspondendo às oitavas 3, 2 e 1. Precisamos destes 12 valores no total. Embora o original tenha somente oito valores, não precisamos nos preocupar com a diferença no tamanho. Os dados só cresceram em um valor por operação de filtragem, o que seria o caso com esta wavelet em particular caso tivéssemos oito ou oito milhões de amostras em nossos dados originais. Este exemplo demonstra como podemos calcular a DWT por conta própria, ou, melhor ainda, como podemos instruir o computador a fazê-lo.

8.12

Execução da DWT com Matrizes

Esta seção mostrará como a DWT pode ser computada com matrizes. Consideraremos somente um canal, pois o segundo canal trabalhará da mesma maneira e a soma matricial é de fácil execução. Para o segundo canal, poderíamos criar uma matriz separada ou reutilizar a mesma entrelaçando os canais ou anexando um ao final do outro. O programa DWTmatrix.m demonstra como as matrizes podem ser utilizadas para executar a DWT unidimensional. Existem diversas variações deste programa: a) para utilizar a wavelet Daubechies 4 ou a transformada aditiva/subtrativa (efetivamente a wavelet de Haar, quando os coeficientes são escalonados), b) para escalonar o resultado ou utilizar subamostragem e, por fim, c) para visualizar as operações passa-baixas e passa-altas como operações separadas ou combiná-las em uma matriz.

8.12.1

Uma Breve Análise da Multiplicação Matricial

Primeiramente, vamos examinar uma matriz multiplicada por um vetor.

Para facilitar nossa abordagem, vamos rotular a matriz e os vetores com letras maiúsculas. Ou seja, chamaremos o vetor com valores yi de Y, o vetor com valores xi de X, e a matriz de valores ci de C. Assim a equação anterior será:

de acordo com esta notação. Você pode notar que o vetor X possui zeros para as últimas três posições. Se você for particularmente observador, também poderá notar que as últimas três colunas de C realmente não são necessárias. Em breve veremos o porquê disso. Por ora, lembre-se de como funciona a multiplicação de uma matriz por um vetor. Se contarmos as linhas e colunas da matriz C, veremos que ela possui 11 linhas e 11 colunas. Da mesma forma, vemos 11 valores tanto em Y quanto em X. Como X possui 11 linhas e 1 coluna, nós o consideraremos uma matriz 11 × 1.

Weeks 008.indd 271

02/05/12 16:40

272

CAPÍTULO 8

A multiplicação de C por X significa que multiplicamos uma matriz 11 × 11 por uma medindo 11 × 1. Visto que o número de colunas de C corresponde ao número de linhas em X, a operação funcionará. Visualize o vetor movendo-se para cima, sobre a matriz, girando, e seus elementos movendo-se para baixo pelas colunas como gotas de chuva descendo pelo lado de uma janela. Se tivéssemos múltiplas colunas em X, faríamos isso repetidamente, uma coluna por vez, e manteríamos os resultados como colunas separadas. O resultado de Y = CX segue abaixo.

Uma consequência interessante é que os valores Y são os mesmos que obteríamos se convolvêssemos a sequência {c0, c1, c2, c3} com {x0, x1, x2, x3, x4, x5, x6, x7}. Com valores reais, encontramos os produtos internos destas duas sequências. A estrutura de um filtro FIR com coeficientes {c0, c1, c2, c3} e os valores xi como entradas nos forneceria as mesmas saídas. Da mesma forma que os filtros FIR executam convolução, podemos utilizar matrizes para fazer a mesma coisa. Esta é a ideia central para esta seção.

8.12.2

Aplicação da DWT

Vimos como computar a DWT com filtros FIR. Vimos que uma matriz construída apropriadamente multiplicada por um vetor nos fornece os mesmos resultados que os de um filtro FIR. Agora veremos como combinar duas multiplicações matriciais para encontrar a DWT. Na subseção anterior, mencionamos que as últimas três colunas da matriz C eram desnecessárias. Isso se deve ao fato de termos inserido três valores zero em nosso sinal X. Deste modo, os cálculos para os valores ci diferentes de zero nas colunas mais à direita são sempre ci multiplicado por zero. As razões pelas quais inserimos zeros nos valores X são: a) fazer com que a saída tenha o mesmo tamanho que a entrada; e b) sugerir como o padrão de valores ci iria se repetir para entradas muito maiores. Além disso, esses valores extra tornam a matriz C quadrada. A matriz C multiplicada por X gera um conjunto de saídas que desejamos (ou seja, a saída para, digamos, o filtro passa-baixas). Portanto, poderíamos utilizar uma segunda multiplicação matricial para encontrar a saída do filtro passa-altas. Manteremos estas separadas até combiná-las para a reconstrução. O código a seguir mostra como podemos definir a saída passa-baixas utilizando a wavelet de Daubechies de 4 coeficientes. Primeiramente, definimos os coeficientes de filtro que desejamos. % Coeficientes passa-altas (wavelet) d0 = (1-sqrt(3))/(4*sqrt(2)); d1 = -(3-sqrt(3))/(4*sqrt(2)); d2 = (3+sqrt(3))/(4*sqrt(2)); d3 = -(1+sqrt(3))/(4*sqrt(2)); % Coeficientes passa-baixas (escalonamento) c0 = -d3; c1 = d2; c2 = -d1; c3 = d0;

Weeks 008.indd 272

02/05/12 16:40

A Transformada Discreta de Fourier

273

Além disso, precisamos de um sinal de exemplo para utilizar. O sinal efetivamente possui apenas oito valores. x = [ 13 1 9 6 4 9 8 4 0 0 0 ]; Agora inserimos estes em uma matriz, exatamente como nossa matriz C conforme mostrado anteriormente. % Passa-baixas L = [c0 0 0 0 c1 c0 0 0 c2 c1 c0 0 c3 c2 c1 c0 0 c3 c2 c1 0 0 c3 c2 0 0 0 c3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

0 0 0 0 c0 c1 c2 c3 0 0 0

0 0 0 0 0 c0 c1 c2 c3 0 0

0 0 0 0 0 0 c0 c1 c2 c3 0

0 0 0 0; ... 0 0 0 0; ... 0 0 0 0; ... 0 0 0 0; ... 0 0 0 0; ... 0 0 0 0; ... 0 0 0 0; ... c0 0 0 0; ... c1 c0 0 0; ... c2 c1 c0 0; ... c3 c2 c1 c0];

Executamos um conjunto similar de operações para a saída passa-altas, a saber, a definição de uma matriz H utilizando os valores di anteriores. As saídas são encontradas de forma simples, utilizando multiplicação. Nós a rotulamos top e bottom para os canais superior e inferior, respectivamente. (Note que o código implica que o filtro passa-baixas encontra-se no canal superior, quando muitos desenhos mostramno no canal inferior. Isso não tem importância, desde que sejam consistentes.) top = L * x.' bottom = H * x.' Para encontrarmos a transformada inversa, precisamos de duas matrizes inversas (uma para a reconstrução passa-baixas e uma para a passa-altas). % Reconstrucao LR = [c3 0 0 c2 c3 0 c1 c2 c3 c0 c1 c2 0 c0 c1 0 0 c0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

passa-baixas 0 0 0 0 0 0 0 0; ... 0 0 0 0 0 0 0 0; ... 0 0 0 0 0 0 0 0; ... c3 0 0 0 0 0 0 0; ... c2 c3 0 0 0 0 0 0; ... c1 c2 c3 0 0 0 0 0; ... c0 c1 c2 c3 0 0 0 0; ... 0 c0 c1 c2 c3 0 0 0; ... 0 0 c0 c1 c2 c3 0 0; ... 0 0 0 c0 c1 c2 c3 0; ... 0 0 0 0 c0 c1 c2 c3];

Definimos uma matriz similar, HR, para os valores passa-altas. Portanto, os vetores que definem a reconstrução também são encontrados com a multiplicação. top_recon = LR * top; bottom_recon = HR * bottom; Por fim, combinamos os dois canais. A divisão por dois é necessária para que os resultados sejam escalonados apropriadamente. Como não utilizamos a subamostragem, os coeficientes de filtro que empregamos produzem saídas com o dobro da magnitude. x_hat = (top_recon + bottom_recon)/2;

Weeks 008.indd 273

02/05/12 16:40

274    CAPÍTULO 8 Quando computamos a reconstrução, obtemos os valores a seguir. As saídas são arredondadas, apenas para eliminar as partes fracionárias da impressão. Para verificar isso por si mesmo, execute o programa DWTmatrix.m. Sinal original: 13 1 9 6 4

9

8

4

0

0

0

Sinal reconstruido: 0 0 0 13 1 9 6

4

9

8

4

A saída é igual à entrada, exceto pelo fato de ser deslocada por 3 unidades.

  8.12.3   Subamostragem com Matrizes A divisão por dois durante a reconstrução se deve aos coeficientes, pois eles foram criados para serem utilizados em um sistema com subamostragem. A etapa seguinte, portanto, é aplicar a subamostragem nesta transformada com operações matriciais. Se tivéssemos um filtro FIR, poderíamos implementar subamostragem de uma forma simples (porém ineficiente) meramente descartando cada valor alternado (um sim, outro não). Para o vetor Y anterior, isso significa que teríamos {y0, y2, y4, y6, y8, y10}. Note como cada linha em C produz uma amostra em Y. Se desejarmos eliminar cada valor yi alternado, poderemos consegui-lo descartando cada linha alternada em C. % Passa-baixas L = [c0 0 0 0 0 0 0 0 0 0 0; ... c2 c1 c0 0 0 0 0 0 0 0 0; ... 0 c3 c2 c1 c0 0 0 0 0 0 0; ... 0 0 0 c3 c2 c1 c0 0 0 0 0; ... 0 0 0 0 0 c3 c2 c1 c0 0 0; ... 0 0 0 0 0 0 0 c3 c2 c1 c0]; A matriz de reconstrução passa-baixas se parece com esta a seguir. Ela é a mesma de antes, com cada coluna alternada removida. % Reconstrucao LR = [c3 0 0 c2 0 0 c1 c3 0 c0 c2 0 0 c1 c3 0 c0 c2 0 0 c1 0 0 c0 0 0 0 0 0 0 0 0 0

passa-baixas 0 0 0; ... 0 0 0; ... 0 0 0; ... 0 0 0; ... 0 0 0; ... 0 0 0; ... c3 0 0; ... c2 0 0; ... c1 c3 0; ... c0 c2 0; ... 0 c1 c3];

Esta é a ideia básica. Faríamos o mesmo para o passa-altas e as matrizes de reconstrução passa-altas. A etapa de reconstrução final não precisaria do fator de escalonamento. x_hat = (top_recon + bottom_recon); Ao executarmos este programa (DWTmatrix_v2.m), constatamos que obtemos a mesma saída que a entrada, apenas deslocada por três amostras.

Weeks 008.indd 274

07/05/12 14:39

A Transformada Discreta de Fourier

8.12.4

275

Combinação de Passa-Baixas e Passa-Altas em uma Matriz

Até aqui mantivemos os canais passa-baixas e passa-altas separados utilizando matrizes diferentes. Mas podemos combinar os dois facilmente colocando um abaixo do outro. % Passa-baixas M = [c0 0 0 0 c2 c1 c0 0 0 c3 c2 c1 0 0 0 c3 0 0 0 0 0 0 0 0 % Passa-altas d0 0 0 0 d2 d1 d0 0 0 d3 d2 d1 0 0 0 d3 0 0 0 0 0 0 0 0

0 0 0 0 0 0 0; 0 0 0 0 0 0 0; c0 0 0 0 0 0 0; c2 c1 c0 0 0 0 0; 0 c3 c2 c1 c0 0 0; 0 0 0 c3 c2 c1 c0;

... ... ... ... ... ...

0 0 0 0 0 0 0; ... 0 0 0 0 0 0 0; ... d0 0 0 0 0 0 0; ... d2 d1 d0 0 0 0 0; ... 0 d3 d2 d1 d0 0 0; ... 0 0 0 d3 d2 d1 d0];

Note como a metade superior e a metade inferior de M fazem coisas diferentes. Chamamos o resultado da multiplicação de top_bottom para refletir o fato de que ela combina ambos os canais superior (top) e inferior (bottom). Assim, a operação a seguir executa tanto a filtragem passa-baixas quanto a passaaltas. top_bottom = M * x.' A primeira metade das saídas (top_bottom) corresponde a um canal, enquanto a segunda metade corresponde ao outro. Para encontrarmos a transformada inversa, precisamos primeiramente de uma matriz de reconstrução apropriada. % Reconstrucao passa-baixas MR = [c3 0 0 0 0 0 c2 0 0 0 0 0 c1 c3 0 0 0 0 c0 c2 0 0 0 0 0 c1 c3 0 0 0 0 c0 c2 0 0 0 0 0 c1 c3 0 0 0 0 c0 c2 0 0 0 0 0 c1 c3 0 0 0 0 c0 c2 0 0 0 0 0 c1 c3

Reconstrucao passa-altas d3 0 0 0 0 0; ... d2 0 0 0 0 0; ... d1 d3 0 0 0 0; ... d0 d2 0 0 0 0; ... 0 d1 d3 0 0 0; ... 0 d0 d2 0 0 0; ... 0 0 d1 d3 0 0; ... 0 0 d0 d2 0 0; ... 0 0 0 d1 d3 0; ... 0 0 0 d0 d2 0; ... 0 0 0 0 d1 d3];

Agora que os coeficientes de escalonamento e de wavelet compartilham as mesmas linhas, elas interagirão uma com a outra. A linha superior talvez seja a mais fácil de utilizar como um exemplo. O vetor top_ bottom contém os dados no domínio da wavelet. Quando os multiplicamos pela matriz de reconstrução MR, tal como em: x_hat = MR * top_bottom; obtemos o primeiro valor de top_bottom (uma das saídas passa-baixas) e o multiplicamos por c3, assim como o primeiro valor da segunda metade de top_bottom (uma das saídas passa-altas) e o multiplicamos por d3. Todas as demais amostras de dados no domínio da wavelet são multiplicadas por um zero corres-

Weeks 008.indd 275

02/05/12 16:40

276

CAPÍTULO 8

pondente. A etapa final da multiplicação matricial é a soma dos produtos, portanto obtemos a adição dos canais da transformada inversa. Ou seja, o primeiro valor reconstruído é c3 × primeira–saída–passa-baixa + d3 × primeira–saída–passa-alta. Tudo o que fizemos aqui foi passar da adição de uma operação explícita (saída passa-baixas + saída passaaltas) para uma operação implícita como parte da soma durante a multiplicação matricial. O programa DWTmatrix_v6.m utiliza as matrizes acima para executar a transformada direta e a inversa. O programa DWTmatrix_v5.m é similar, embora não utilize subamostragem. Quando multiplicamos a matriz MR pela matriz M, obtemos um resultado interessante. >> disp(round(MR*M)) 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

0 0 0 0 0 0 0 1 0 0 0

0 0 0 0 0 0 0 0 1 0 0

0 0 0 0 0 0 0 0 0 1 0

0 0 0 0 0 0 0 0 0 0 1

0 0 0 0 0 0 0 0 0 0 0

0 0 0 0 0 0 0 0 0 0 0

0 0 0 0 0 0 0 0 0 0 0

Observe como a multiplicação produz uma matriz de identidade, na qual foram inseridas três linhas e três colunas de zeros. Esta é uma das muitas formas de executar a transformada e a transformada inversa. Poderíamos, por exemplo, reorganizar as linhas de M para alternar entre passa-baixas e passa-altas, depois alterar MR para colocar os coeficientes de reconstrução em cada coluna alternada. (O programa DWTmatrix_v6b.m faz isso.) A matriz da transformada direta se pareceria com esta a seguir: M = [c0 0 0 d0 0 0 c2 c1 c0 d2 d1 d0 ...

0 0 0 0

0 0 0 0

0 0 0 0

0 0 0 0

0 0 0 0

0 0 0 0

0 0 0 0

0; 0; 0; 0;

... ... ... ...

% % % %

Passa-baixas Passa-altas Passa-baixas Passa-altas

A matriz da transformada inversa, por sua vez, teria uma estrutura como esta: MR = [c3 d3 0 0 c2 d2 0 0 c1 d1 c3 d3 c0 d0 c2 d2 ...

0 0 0 0

0 0 0 0

0 0 0 0

0 0 0 0

0 0 0 0

0 0 0 0

0 0 0 0

0; 0; 0; 0;

... ... ... ...

Todos os experimentos podem ser realizados com menos (ou mais) coeficientes de filtro e tamanho de entrada. Os programas DWTmatrix_v3.m, DWTmatrix_v3b.m e DWTmatrix_v3c.m são todos eles variações deste tema, utilizando a transformada “aditiva/subtrativa”. Não obstante, quando escalonamos os coeficientes para esta transformada (tal como estes programas fazem), trata-se da mesma coisa que a wavelet de Haar. As variações incluem uma forma “preguiçosa” de se alterar o número de coeficientes. Como só há dois coeficientes, podemos simplesmente configurar valores c2, c3, d2 e d3 como zero. Você não pode multiplicar matrizes A × B a menos que o número de colunas de A seja igual ao número de linhas de B, de modo que a única parte mais complicada é manter os tamanhos compatíveis. Por fim, podemos executar a transformada direta e a inversa juntas em uma instrução: x_hat = MR * (M * x.');

Weeks 008.indd 276

02/05/12 16:40

A Transformada Discreta de Fourier

277

Embora os resultados possivelmente não sejam surpreendentes por envolverem uma grande quantidade de cálculos que poderiam ter sido feitos em uma única instrução de atribuição, eles nos permitem verificar que obtemos uma aproximação precisa de nosso sinal original. Poderíamos construir diferentes coeficientes de filtro (valores ci e di) e montar nossas matrizes M e MR a partir deles, operação esta que nos permitiria verificar nosso trabalho. Também poderíamos verificá-lo computando MR * M sem especificar x. E, o que é mais importante, ele resume simbolicamente o que estamos fazendo.

8.12.5

Generalização da DWT com Matrizes

Primeiramente, considere o canal sem bancos de filtros. Dispomos de dois filtros junto com o canal, um filtro de análise h seguido de um filtro de síntese f. Nossos dados transformados w serão a entrada x convolvida com h.

Em seguida, convolvemos w com f para criar nossa saída de canal.

Assim, a ideia aqui se resume a executar convolução com uma operação matricial. Considere um único valor, wn, oriundo da primeira convolução. Para simplificar as coisas, consideraremos que h possui quatro valores, embora de um modo geral pudéssemos utilizar quantos elementos quiséssemos para h. Assim,

Note que isso pode ser realizado com uma matriz 1 × 4 multiplicada por uma matriz 4 × 1.

Tudo o que precisamos fazer é generalizar a matriz h para todo n.

Podemos criar uma matriz similar com f para convolver com w para o outro filtro. Lidamos com a subamostragem da mesma forma que o fizemos anteriormente. Dispomos apenas de valores w para índices n pares. (Lembre-se de que estamos considerando apenas o filtro de análise por enquanto.) Em outras palavras, considerando que n é par, queremos gerar wn, wn+2, wn+4, etc. Tal como antes, realizamos isso eliminando cada segunda linha na matriz anterior de valores h. Para a matriz como um todo, parece que estamos deslocando os valores h duas posições sempre que descemos uma linha.

Weeks 008.indd 277

02/05/12 16:40

278

CAPÍTULO 8

Passemos agora para a sobreamostragem e a reconstrução. Cada entrada alternada (wn, quando n é ímpar) deve ser igual a zero.

Em vez de inserir zeros entre valores w, preferimos alterar a matriz f. Isso nos leva à matriz f a seguir. Em vez de deslocar os valores f duas vezes entre linhas, nós os escrevemos deslocados para baixo por duas linhas entre colunas. Ao examinarmos os padrões para valores f e h, percebemos a similaridade entre eles e o movimento de um cavalo em um jogo de xadrez.

Ao tomarmos as duas matrizes, H e G, observamos algumas propriedades interessantes [3]. O programa a seguir gera pequenos exemplos para as matrizes passa-baixas (H) e passa-altas (G), com base na wavelet de Daubechies de 4 coeficientes. Veremos que a multiplicação da matriz de escalonamento por uma versão transposta de si mesma resulta na matriz de identidade. Da mesma forma, a multiplicação da matriz de wavelet por uma versão transposta de si mesma também fornece este resultado. Isso mostra que as funções de wavelet e de escalonamento são normalizadas. Quando multiplicamos a matriz de escalonamento pela matriz de wavelet transposta, obtemos zeros – o que mostra que estas funções são ortogonais. Portanto, elas são ortonormais. % Daubechies_coeffs.m % % Coeficientes de wavelet de Daubechies % % Coeficientes passa-altas (wavelet) d0 = (1-sqrt(3))/(4*sqrt(2)); d1 = -(3-sqrt(3))/(4*sqrt(2)); d2 = (3+sqrt(3))/(4*sqrt(2)); d3 = -(1+sqrt(3))/(4*sqrt(2)); % Coeficientes passa-baixas (escalonamento) c0 = -d3; c1 = d2; c2 = -d1; c3 = d0;

Weeks 008.indd 278

02/05/12 16:40

A Transformada Discreta de Fourier

279

H = [c3 c2 c1 c0 0 0 0 0 0 0; ... 0 0 c3 c2 c1 c0 0 0 0 0; ... 0 0 0 0 c3 c2 c1 c0 0 0; ... 0 0 0 0 0 0 c3 c2 c1 c0]; G = [d3 d2 d1 d0 0 0 0 0 0 0; ... 0 0 d3 d2 d1 d0 0 0 0 0; ... 0 0 0 0 d3 d2 d1 d0 0 0; ... 0 0 0 0 0 0 d3 d2 d1 d0]; disp('multiplicando coeficientes de Escalonamento por sua transposicao = I'); H*H.' disp('multiplicando coeficientes de Wavelet por sua transposicao = I'); G*G.' disp('multiplicando coeficientes de Escalonamento por coeficientes de Wavelet = matriz 0'); H*G.' Quando executamos o programa anterior, obtemos a seguinte saída: multiplicando coeficientes de Escalonamento por sua transposicao = I ans = 1.0000 0.0000 0 0

0.0000 1.0000 0.0000 0

0 0.0000 1.0000 0.0000

0 0 0.0000 1.0000

multiplicando coeficientes de Wavelet por sua transposicao = I ans = 1.0000 0.0000 0 0

0.0000 1.0000 0.0000 0

0 0.0000 1.0000 0.0000

0 0 0.0000 1.0000

multiplicando coeficientes de Escalonamento por coeficientes de Wavelet = matriz 0 ans = 1.0e-17 * 0.3648 -0.1306 0 0

-0.1637 0.3648 -0.1306 0

0 -0.1637 0.3648 -0.1306

0 0 -0.1637 0.3648

A partir a execução anterior, vemos que a multiplicação de uma matriz de coeficientes por sua transposição resulta na matriz de identidade, ou seja, HHT = I e GGT = I. Além disso, quando multiplicamos as duas matrizes entre si, obtemos uma matriz zero: HGT = 0. (Não se esqueça de que cada valor da saída final anterior é multiplicado por um expoente muito pequeno. Esses valores diferentes de zero podem ser atribuídos ao erro de precisão.)

Weeks 008.indd 279

02/05/12 16:40

280

CAPÍTULO 8

A transformada de wavelet para três oitavas (níveis de resolução) Entrada

Detalhe 1

2

HPF

Detalhe 2

2

HPF

X[n] 2

LPF

HPF

2

Detalhe 3

LPF

2

Aproximar

2

LPF Análise (transformada direta)

Detalhe 1

2

IHPF Saída

Detalhe 2 Detalhe 3

2 2

2

HPF 2

Aproximar

2

IHPF ILPF

Y[n]

ILPF

LPF

Síntese (transformada inversa) Figura 8.27

Três oitavas da transformada discreta de wavelet unidimensional.

Isso abrange apenas uma oitava da transformada de wavelet. Podemos estender isso a diversas oitavas, bem como a diversas dimensões. Aqui, utilizamos as matrizes de coeficientes para definir a transformada. Podemos utilizá-las recursivamente para computar oitavas adicionais. Ou seja, w = Hx (com H representando a matriz de valores h). A análise do outro canal seria v = Gx (onde G é exatamente como H, exceto pelo fato de ter sido gerado a partir de um filtro diferente). Consideraremos que G representa a matriz composta de coeficientes passa-altas (wavelet) e que H representa uma matriz similar composta dos coeficientes passa-baixas (escalonamento). Utilize a parte superior da Figura 8.27 como referência para a análise de x para três oitavas. Primeiramente, encontraremos a aproximação do primeiro nível. Nós não a manteremos, mas a usaremos para computar as outras oitavas. aproximação1 = Hx Para obtermos a aproximação da oitava seguinte, utilizamos o resultado anterior. aproximação2 = Haproximação1 aproximação2 = H(Hx) Assim, as saídas de três oitavas da DWT seriam:

Podemos calcular estes valores recursivamente, conforme visto aqui, ou podemos criar matrizes de transformação que possuem o mesmo resultado. A título de exercício, tente executar a DWT de três oitavas com matrizes no sinal de exemplo {34, 28, 76, 51, 19, 12, 25, 43, 0, 0, 0} utilizando {1/ , –1/ } como a wavelet. Após concluir, compare sua resposta com o programa DWTmatrix_v8.m, disponível no site da LTC Editora.

Weeks 008.indd 280

02/05/12 16:40

A Transformada Discreta de Fourier

8.13

281

Resposta de Magnitude em Frequência para uma Wavelet

Concluímos este capítulo com um programa que mostra a resposta de magnitude em frequência de uma dada wavelet. O site da LTC Editora fornece uma cópia denominada show_wavelet.m. Em vez de listar o programa inteiro, esta seção apresenta apenas os destaques. Por mostrar a resposta de magnitude em frequência para uma wavelet e sua função de escalonamento, o programa espera que a variável wavelet tenha um valor apropriado. Existem muitas wavelets possíveis para se usar, e encorajamos você a tentar algumas diferentes. Se você dispuser da caixa de ferramentas wavelet do MATLAB, poderá visualizar a resposta de magnitude em frequência para qualquer wavelet definida na parte superior do programa. Além disso, definimos a variável PADSIZE, o número de zeros a acrescentar após os coeficientes de filtro para conferir ao espectro uma boa aparência. wavelet = 'db4'; PADSIZE = 200; Em seguida, tentamos obter o conjunto de coeficientes de wavelet utilizando o comando wfilters. Mas se a caixa de ferramentas wavelet não puder ser encontrada (no caso de estar se utilizando a edição para estudantes, por exemplo), o programa será restabelecido com a wavelet de Daubechies de 4 coeficientes (db2) sendo utilizada como padrão. try [lpf, hpf, ilpf, ihpf] = wfilters(wavelet); catch disp('Utilizando a wavelet padrao, db2.'); wavelet = 'db2'; a = (1-sqrt(3))/(4*sqrt(2)); b = (3-sqrt(3))/(4*sqrt(2)); c = (3+sqrt(3))/(4*sqrt(2)); d = (1+sqrt(3))/(4*sqrt(2)); lpf = [a b c d]; % coeficientes diretos hpf = [-d c -b a]; ilpf = [d c b a]; % coeficientes reversos ihpf = [a -b c -d]; end De qualquer modo, teremos definições para o filtro passa-baixas (lpf), para o filtro passa-altas (hpf) e seus respectivos filtros inversos (ilpf e ihpf). Em seguida, inserimos zeros em cada conjunto de coeficientes. Isso imita a passagem de uma função impulso com muitos zeros posteriores através de um filtro. lpf(length(lpf)+1:PADSIZE) = 0; Uma vez que dispomos das versões contendo zeros, podemos encontrar a transformada rápida de Fourier. Por conveniência, utilizamos caracteres maiúsculos no nome para os dados de frequência e definimos as magnitudes (por exemplo, LPFmag) e os ângulos de fase (por exemplo, LPFphase). Na prática, não utilizamos os ângulos de fase, mas o leitor pode experimentá-los livremente. LPF = fft(lpf); LPFmag = abs(LPF); LPFphase = angle(LPF); Ao traçarmos o espectro, mostramos somente a primeira metade da faixa de frequência, presumindo que os dados de entrada (os coeficientes de filtro) são todos reais. As duas linhas a seguir definem range, uma lista que especifica metade das frequências. half_len = ceil(length(LPF)/2); range = 1:half_len;

Weeks 008.indd 281

02/05/12 16:40

282

CAPÍTULO 8

Com as entradas preenchidas com zeros até a extensão padrão 200, o eixo x mostrará uma faixa até metade disso. Como não temos uma frequência de amostragem, não faz sentido representar o eixo x em termos de Hz. Em vez disso, podemos encará-lo como uma percentagem. Ao gerarmos o traçado, as magnitudes variarão de zero até algum número positivo. Este número não contém qualquer informação útil. Ele advém da soma dos valores utilizados. Assim sendo, escalonamos a faixa de zero a um para gerar um traçado de boa aparência. Para realizarmos isso, precisamos conhecer o valor máximo, que deve ser um valor na frequência zero. LPFmagMAX = max(LPFmag); Escalonamos as magnitudes dividindo-as por este valor. Enfim, estamos prontos para exibir os espectros. O comando a seguir traça a resposta de frequência do filtro passa-baixas sobre aquela do filtro passa-altas.

Resposta de magnitude em frequência de filtros passa-baixas (sólida) e passa-altas (pontilhada)

1 0.9 0.8 0.7 0.6 0.5 0.4 0.3 0.2 0.1 0 0

1

10

20 30 40 50 60 70 80 wavelet = db4, com oito coeficientes

90

100

Figura 8.28 O primeiro gráfico do programa show–wavelet.

Resposta de magnitude em frequência de filtros passa-baixas inverso (sólida) e passa-altas inverso (pontilhada)

0.9 0.8 0.7 0.6 0.5 0.4 0.3 0.2 0.1 0 0

Weeks 008.indd 282

10

20 30 40 50 60 70 80 wavelet = db4, com oito coeficientes

90

100

Figura 8.29 O segundo gráfico do programa show–wavelet.

02/05/12 16:40

A Transformada Discreta de Fourier

283

plot(range, LPFmag(range)/LPFmagMAX,'r', ... range, HPFmag(range)/HPFmagMAX,'b'); O resto do programa melhora a aparência do gráfico acrescentando títulos e rótulos. Ao utilizarmos a wavelet db4, obtemos os gráficos exibidos nas Figuras 8.28 e 8.29. Para que possamos visualizar as respostas de magnitude em frequência de outras wavelets, a caixa de ferramentas wavelet deve estar instalada. Estas figuras mostram o que devemos esperar: o filtro passa-baixas (e o filtro passabaixas inverso) preserva as frequências baixas, mas atenua as frequências altas.

RESUMO . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Este capítulo apresenta a transformada discreta de wavelet e mostra como filtros FIR comuns podem ser utilizados para implementá-la. Examinamos a transformada de Haar, uma transformada de wavelet simples de dois coeficientes de filtro, e as wavelets de Daubechies em seguida, como uma extensão da transformada de Haar. As wavelets biortogonais também foram apresentadas. O banco de filtros pode ser visualizado como uma forma de executar esta transformada. Dois tipos comuns de bancos de filtros são os filtros espelhados em quadratura e os filtros em quadratura conjugada. Este capítulo examinou os subamostradores e os sobreamostradores que acompanham um banco de filtros, bem como a multirresolução, onde decompomos um sinal em escalas cada vez menos refinadas. A multirresolução corresponde vagamente à duplicação de frequência presente na música, dando origem ao termo oitavas de resolução. Vimos também como a transformada de wavelet pode ser executada com operações matriciais. As funções para a execução desta transformada incluem a dwt e a idwt para as transformadas discretas de wavelet direta e inversa, respectivamente. Qualquer um interessado em processamento de imagem devem considerar os comandos dwt2 e idwt2.

EXERCÍCIOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1. Calcule (manualmente) como os sinais zd[n] e wd[n] seriam para o banco de filtros na Figura 8.12. Considere x = {8, 4, 0, 6, 3, 7, 2, 9}, a = 1/2 e b = 1/2. Certifique-se de mostrar seu trabalho. 2. Com um banco de filtros de dois canais tal como o da Figura 8.13, encontramos a saída y[n] = (ac + bd)x[n − 1] + (aa + bb + cc + dd)x[n − 3] + (ac + bd)x[n − 5]. A transformada de wavelet de Daubechies utiliza os coeficientes

(a) Que restrições a equação anterior impõe sobre os coeficientes? Mostre que os coeficientes de Daubechies satisfazem estas restrições. (b) Que efeito os sub/sobreamostradores exercem sobre a saída y[n]? Como a remoção dos mesmos mudaria a equação y[n] anterior? 3. O que é multirresolução (ou seja, uma transformada de wavelet com mais de uma oitava)? Demonstre esta ideia com uma figura. 4. Para os coeficientes de filtro a seguir, utilize MATLAB para traçar a resposta de magnitude em frequência e a resposta de fase. Determine se os filtros possuem fase linear e se são passa-altas, passabaixas, passa-banda ou rejeita-banda. (a) {−0.0884, 0.0884, 0.7071, 0.7071, 0.0884, −0.0884} (b) {−0.1294, 0.2241, 0.8365, 0.4830} (c) {−0.4830, 0.8365, −0.2241, −0.1294}

Weeks 008.indd 283

02/05/12 16:40

284    CAPÍTULO 8 5. Escreva um programa MATLAB que execute a transformada (para uma oitava) e a transformada inversa a seguir. Examine a Figura 8.1, em que LPF = {1, 1}, HPF = {−1, 1}. (LPF significa filtro passa-baixas e HPF significa filtro passa-altas.) Para a transformada inversa, examine a Figura 8.2. Em que ILPF = {−1, −1}, IHPF = {−1, 1}. (ILPF significa filtro passa-baixas inverso e IHPF signi­fica filtro passa-altas inverso.) Será y uma versão escalonada de x – por exemplo, y[n] × (−1/4) = x[n]? 6. Implemente uma transformada de Haar com comandos MATLAB, para três oitavas. 7. Vimos um padrão para um CQF, no qual os coeficientes de um filtro são alternadamente negativados e espelhados, de modo que, se conhecermos os coeficientes de um filtro, poderemos encontrar os coeficientes para os outros três. Isso funciona de modo generalizado? Isso funcionaria para a = 3, b = 1 na Figura 8.5? 8. Se os subamostradores preservam somente um em cada quatro valores, que efeito isso teria no banco de filtros? O que aconteceria se a estrutura do banco de filtros fosse modificada para ter quatro canais? 9. Escreva uma função que retorne a primeira oitava, transformada de wavelet de Daubechies de 4 coeficientes para um dado sinal. Inclua saídas passa-baixas e passa-altas. 10. Dado o sinal de entrada x[n] = 2cos(2p100n/300) + cos(2p110n/300 − p/8) para n = 0.255, escreva os comandos para encontrar a DWT para três oitavas. Compare seus resultados com aqueles da função dwt. Trace a função original, bem como os sinais aproximados. 11. Para a transformada de Haar exibida na Figura 8.5, utilize valores a = b = ½ (sem subamostragem), encontre os sinais z, w e y, dada uma entrada x = {6, 1, 3, 7, 2, 5, 8, 10}. 12. Suponha que você tenha uma DWT de três oitavas. Desenhe a estrutura de análise em termos de filtros e subamostradores. 13. Para uma DWT de quatro oitavas, suponha que a entrada tenha 1024 amostras. Quão extensas as saídas de detalhe seriam? Quão extensas as saídas de aproximação seriam? E se a sub/sobreamostragem não fosse utilizada? Para simplificar, você pode considerar que as saídas e as entradas do filtro possuem a mesma extensão. 14. Para uma entrada de 1024 amostras, quantas oitavas da DWT poderíamos ter antes que a aproximação se tornasse um único número? Para simplificar, você pode considerar que as saídas e as entradas do filtro possuem a mesma extensão. 15. Afirmamos anteriormente que o filtro espelhado em quadratura não funcionará com quatro coefi~ ~ cientes, ou seja, h = h = {a, b, c, d}, g = {a, −b, c, −d}, g = −g. Mostre analiticamente que isso não funcionará. Ou seja, que restrições existem sobre os valores a, b, c e d para que se obtenha uma reconstrução perfeita?

  PROJETO  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Para um dado sinal, encontre a contribuição de cada canal para quatro oitavas da DWT. Calcule a energia dos subsinais como a soma de cada valor ao quadrado. Que canal possui a maior energia? O que aconteceria se você tivesse frequências baixas, médias e altas no sinal original? Em que canais estas frequências aparecem? Um exemplo de solução é fornecido no site da LTC Editora, como project8.m. Tente resolver este problema por conta própria antes de ler o texto a seguir.

Este projeto pede algo muito similar ao programa addwaves3, discutido anteriormente neste capítulo. O programa, disponível no site da LTC Editora, toma um sinal de entrada e encontra a transformada de wavelet correspondente para três oitavas. Em seguida, ele reconstrói cada canal separadamente e traça

Weeks 008.indd 284

07/05/12 14:40

A Transformada Discreta de Fourier

285

as reconstruções. Assim, a primeira etapa da execução deste projeto é ampliar o programa addwaves3 e acrescentar uma quarta oitava à decomposição. Chamaremos a nova versão do programa de addwaves4. A decomposição em três oitavas fornece dados rotulados como L e H para a primeira oitava, LL e HH para a segunda oitava e LLL e HHH para a terceira oitava. Utilizamos a aproximação da terceira oitava, LLL, para criar a quarta oitava, como segue. Considere que a wavelet db2 é utilizada para nos fornecer os coeficientes de filtro passa-baixas LPF e os coeficientes de filtro passa-altas HPF. LLLL = downsample(myconv(LLL, LPF)); LLLH = downsample(myconv(LLL, HPF)); Para a reconstrução, deixamos wave2, wave3 e wave4 intocados, pois estes fornecem as versões reconstruídas dos três canais superiores. Mudaremos wave1 e acrescentaremos um novo subsinal wave0, como segue: wave0 = ILow(ILow(ILow(ILow(LLLL)))); wave1 = ILow(ILow(ILow(IHigh(LLLH)))); A lista wave0 representa a aproximação reconstruída na quarta oitava, enquanto wave1 contém o detalhe reconstruído naquela oitava. Os comandos de traçado precisam ser atualizados para acrescentar wave0 e wave1, mas isso não é problema. Voltamos agora nossa atenção para um novo aspecto deste projeto, calculando a energia para cada subsinal. Se elevarmos cada elemento das listas ao quadrado e somarmos os resultados, obteremos a energia para o subsinal. Eis, por exemplo, o comando para se encontrar a energia para wave0. energy0 = sum(wave0.*wave0); Da mesma forma, encontramos as energias dos demais subsinais. Isso conclui o programa para addwaves4. O enunciado do problema também especifica que consideremos as frequências baixas, médias e altas no sinal de exemplo. Um bom modo de testarmos isso é experimentando a função addwaves4 com diversas senoides diferentes. Faremos uma lista de frequências para experimentar, percorreremos a lista criando um sinal de exemplo com aquela frequência e chamaremos addwaves4 com ele em seguida. O código para tal parece-se com este: n = 1:100; % amostras discretas Ts = 200; % periodo de amostragem freqs = [1, 10, 20, 40, 60, 80]; for k = 1:length(freqs) f = freqs(k); input_signal = cos(2*pi*f*n/Ts); addwaves4(input_signal, wavelet); end Para cada frequência na lista freqs, encontramos a função cosseno correspondente para uma lista de valores (n). Depois, quando chamamos addwaves4, examinamos o gráfico e os cálculos de energia. Um acréscimo interessante seria a inclusão de uma pausa entre iterações do loop (ou seja, pause(1)), de modo que o usuário tivesse uma chance de visualizar os gráficos. Com isto em vigor, o programa está concluído. Nós o chamamos de project8.m. Uma coisa interessante ocorre quando esse programa é executado. A maior medição de energia ocorre em canais diferentes à medida que a frequência fica mais alta. Vemos altas energias em LLLL para baixas frequências e, à medida que a frequência fica mais alta, a energia máxima aparece em LLLH, depois em LLH, depois em LH e finalmente em H. Isso é esperado, pois a transformada discreta de wavelet grosso modo divide um sinal em seus componentes passa-baixas e passa-altas em cada oitava. Os detalhes de cada oitava contêm uma faixa de frequências diferente.

Weeks 008.indd 285

02/05/12 16:40

Weeks 008.indd 286

02/05/12 16:40

A Transformada Contínua de Wavelet

9

A

transformada contínua de wavelet (continuous wavelet transform — CWT) revela muito da teoria por trás das wavelets. Neste capítulo, exploraremos a análise de um sinal, o deslocamento e o escalonamento e como executar a CWT em um computador digital com a wavelet Chapéu Mexicano como um exemplo corrente. Existem outras wavelets contínuas, mas, uma vez que você aprenda como a transformada funciona para o Chapéu Mexicano, será capaz de aplicar qualquer wavelet.

9.1

A Wavelet Chapéu Mexicano

Uma wavelet contínua comumente utilizada é o Chapéu Mexicano. A equação a seguir define essa função [2]. (9.1) Aqui, utilizamos ␺ intencionalmente para representar a função, pois essa letra grega frequentemente é empregada para funções wavelet. Ao falarmos sobre wavelets em geral, a função ␺ poderia ser uma das muitas possibilidades. Aqui, utilizaremos a função Chapéu Mexicano como nossa wavelet de exemplo. Conforme mostrado na Figura 9.1, a função Chapéu Mexicano efetivamente lembra um sombreiro. A função possui dois pequenos logos negativos em ambos os lados de um pico alto, centralizado em zero. Apenas olhando a forma da função, podemos conjecturar que as áreas positivas e as áreas negativas cancelam umas as outras. Além disso, a função possui valor zero em sua maior parte, exceto para a pequena área em torno de t = 0. Essas duas observações são importantes.

A função Chapéu Mexicano

1

0.5

0

–0.5 –10

–8

–6

–4

–2

0

2

4

6

8

10

Figura 9.1 A função Chapéu Mexicano

287

Weeks 009.indd 287

02/05/12 16:19

288

CAPÍTULO 9

Primeiramente, um requisito para uma wavelet é que ela tenha uma integral igual a zero: (9.2) Então, nossa função Chapéu Mexicano possui uma integral igual a zero? Poderíamos integrar a função para verificar isso matematicamente, mas também podemos fazê-lo por meio de um programa curto. Vamos considerar que nossa variável de tempo t varia de −10 a +10, com incrementos de 1/100 segundo. >> t = -10:0.01:10; >> mexhat = (1 - t.*t) .* exp(-t.*t/2); >> sum(mexhat) ans = 3.9092e-15 A soma total é igual a 0.00000000000000391, zero, exceto para algum erro de precisão. A segunda observação sobre a função é que ela possui valor zero fora de um pequeno intervalo (aproximadamente −4 a +4). Em outras palavras, ela está bem localizada no tempo. Daubechies afirma que ela também está bem localizada na frequência [2]. Isso pode ser demonstrado, novamente com o MATLAB. A linha a seguir encontra a transformada rápida de Fourier nos valores do Chapéu Mexicano que computamos. MEXHAT = fft(mexhat); Vemos os resultados em dois traçados. A Figura 9.2 mostra as frequências presentes, enquanto a Figura 9.3 exibe uma vista ampliada das primeiras frequências. Tal como essas figuras mostram, a função Chapéu Mexicano está bem localizada na frequência. Você pode notar que a transformada de Fourier do Chapéu Mexicano é zero na frequência zero, ou seja, o componente DC possui valor zero. Isso satisfaz a condição de admissibilidade, um critério importante para que a transformada de wavelet seja reversível [2]. Agora vamos falar especificamente da transformada contínua de wavelet. Dispomos de uma função ␺ (neste exemplo utilizaremos o Chapéu Mexicano) e definimos deslocamentos e escalonamentos com base nela. Em outras palavras, introduzimos os parâmetros u e s que nos permitem alterar a função. Esta equação tem origem no livro de Mallat [40]. (9.3)

1 0.9

Magnitude Relativa

0.8 0.7 0.6 0.5 0.4 0.3 0.2 0.1 0 0

Weeks 009.indd 288

5

10

15

20 25 30 Frequências (Hz)

35

40

45

50

Figura 9.2 Frequências presentes na função Chapéu Mexicano.

02/05/12 16:19

A Transformada Contínua de Wavelet

289

1 0.9

Magnitude Relativa

0.8 0.7 0.6 0.5 0.4 0.3 0.2 0.1 0 0 Figura 9.3

0.1

0.2

0.3

0.4 0.5 0.6 Frequências (Hz)

0.7

0.8

0.9

1

Visão ampliada das primeiras frequências presentes na função Chapéu Mexicano.

Por outro lado, algumas fontes utilizam a no lugar de s e b no lugar de u. A equação equivalente se torna:

Com essa família de equações ␺, definimos agora a transformada contínua de wavelet de nosso sinal f(t) [40]. (9.4) Mas o que isso significa? Vamos trabalhar um pouco com essas equações para entender melhor. Vamos considerar que nossa função ␺ é um valor real. Na prática existem wavelets complexas, mas as coisas ficarão mais simples se limitarmos nossa análise às funções ␺ reais. Essa premissa simplificadora significa que ␺*, o complexo conjugado, possui o mesmo valor que ␺.

9.2

Deslocamento do Chapéu

Vimos a função Chapéu Mexicano na Figura 9.1 e, portanto, conhecemos a forma da função. Mas o que dizer de ␺(t − u/s)? Como é sua aparência? Naturalmente, isso depende dos valores que escolhemos para u e s. Por ora escolhemos s = 1, só para termos uma ideia de como o parâmetro u afeta as coisas. Isso significa que estamos olhando para ␺(t − u/1), ou simplesmente ␺(t − u). A confusão começa quando reorganizamos as variáveis, de modo que precisamos prosseguir com cuidado. A variável t aparece de acordo com a convenção na equação para ␺(t). Mas poderíamos efetivamente utilizar qualquer variável que desejássemos. Ou seja, poderíamos utilizar x no lugar de t, como em:

Também podemos utilizar nomes de variável diferentes com um programa. A função curta a seguir calcula a função Chapéu Mexicano; ela deve ser salva sob o nome de arquivo mexicanhat.m. Trata-se de uma implementação direta da Equação 9.1 utilizando .*, de modo que ela funcionará com listas. function mexhat = mexicanhat(t) mexhat = (1 - t.*t) .* exp(-t.*t/2);

Weeks 009.indd 289

02/05/12 16:19

290

CAPÍTULO 9

Tudo que precisamos fazer é chamá-la com um número ou uma faixa de números. Aqui, nós a chamaremos com um único número (escalar). disp(mexicanhat(0)); Também podemos passar uma faixa de valores para ela. plot(mexicanhat(-10:0.01:10)); Ou poderíamos definir uma faixa, tal como nossa variável t anterior, e utilizá-la. t = -10:0.01:10; myarray = mexicanhat(t); Em relação à nossa presente discussão, poderíamos utilizar qualquer nome de variável que desejássemos ao chamarmos a função; o papel de t é simplesmente o de reservar espaço. Este código funciona igualmente bem. x = -10:0.01:10; myarray = mexicanhat(x); Quer tenhamos em vista um programa que calcula a função Chapéu Mexicano ou a equação para ela, temos a liberdade de passar o parâmetro que quisermos. Ao examinar ␺(t − u), pense no termo t − u como algo a ser comparado ao original t. No traçado da função, vemos que o valor máximo (1) ocorre em t = 0. Podemos verificar que ␺(0) = 1 fazendo o computador calcular mexicanhat(0) ou podemos examinar a função a seguir.

Função Chapéu Mexicano para t – 3

1

0.5

0

–0.5 –10

–8

Figura 9.4

Weeks 009.indd 290

–6

–4

–2

0

2

4

6

8

10

Função Chapéu Mexicano para um parâmetro t − 3.

02/05/12 16:19

291

A Transformada Contínua de Wavelet

Uma vez que função retorna 1 quando o parâmetro é 0, o valor 1 deve aparecer quando t − u = 0. Se movermos u para o outro lado da equação, esperaremos o valor máximo (1) quando t = u. Ao subtrairmos u de t, os valores que a função ␺ retorna não são alterados. A única coisa que se altera é o instante em que a função os retorna, em relação ao t original. A Figura 9.4 exibe um traçado da função ␺(t − 3), com t no eixo x. Ela mostra a função Chapéu Mexicano, centralizada em torno de t − 3. Assim, encare ␺(t − u) como a mesma função que ␺, apenas atrasada em u unidades.

9.3

Escalonamento do Chapéu

Agora vamos examinar o parâmetro s. Considere u = 0, de modo que possamos ignorá-lo momentaneamente, tornando ␺(t − u/s) igual a ␺(t − 0/s), ou simplesmente ␺(t/s). Visto que 0/s = 0, essa função retorna 1 quando t = 0. O lobo principal do Chapéu Mexicano não se move, mas apresenta uma aparência diferente. Para verificarmos isso, examinaremos alguns valores de s. Primeiramente, quando s = 1, nada novo acontece: temos apenas ␺(t) como antes. Mas, quando s < 1 ou s > 1, as coisas começam a ficar interessantes. O índice t passa a ser multiplicado por uma fração ou por um fator. (Não levamos em consideração valores de s menores ou iguais a zero.) Como a multiplicação de nosso índice muda as coisas? A chave é considerar os valores da função em relação ao índice original. O índice contínuo possui um número infinito de valores, e a multiplicação por uma constante não muda isso. Seu efeito real é fazer com que o sinal pareça mais curto ou mais longo, pois a área de suporte (onde ele possui valores diferentes de zero) muda. Vimos no traçado original da função Chapéu Mexicano que ela é igual a zero, exceto para uma pequena área em aproximadamente −4 a +4. Se multiplicássemos o índice por uma constante positiva – digamos 2 –, a função nos retornaria valores diferentes de zero para quase tudo entre −8 e +8, aproximadamente. Trata-se de um mapeamento: aquilo que a função utilizou para retornar em t = 1 agora retorna em t = 2. Em geral, o que a função calcularia para t = k agora calcula para t = 2k. O resultado faz com que a porção diferente de zero da função ␺ se estenda. Ela parece mais larga. Note que o valor de s correspondente seria 1/2, visto que t/s = 2t somente quando s = 1/2. Quando multiplicarmos nosso índice t por uma fração – digamos, 1/2 (correspondendo a s = 2) –, a função retornará valores diferentes de zero para uma faixa reduzida. Em vez do Chapéu Mexicano variando de −4 a +4 aproximadamente, teremos metade da faixa, de −2 a +2, aproximadamente. Assim, a função tem uma forma mais estreita. A Figura 9.5 mostra a função Chapéu Mexicano para três parâmetros t diferentes. Vemos a função Chapéu Mexicano original (␺(t)) na parte superior, seguida por ␺(t/2) na parte central e ␺(2t) na parte inferior. Note que o eixo x mostra o t original, com os valores retornados pela função ao longo do eixo y.

Função Chapéu Mexicano para t

1 0.5 0 –0.5 –10

–8

–6

–4

–2

0

2

4

6

8

10

6

8

10

6

8

10

Função Chapéu Mexicano para t/2

1 0.5 0 –0.5 –10

–8

–6

–4

–2

0

2

4

Função Chapéu Mexicano para t*2

1 0.5 0 Figura 9.5 A função Chapéu Mexicano para diferentes parâmetros t.

Weeks 009.indd 291

–0.5 –10

–8

–6

–4

–2

0

2

4

02/05/12 16:19

292

9.4

CAPÍTULO 9

Deslocamento e Escalonamento: Wavelets

Agora que já vimos como u atrasa a função (deslocando-a ao longo do eixo x) e como s escalona a função tornando-a mais estreita ou mais larga, combinamos os dois. Sua utilização permite que desloquemos e escalonemos simultaneamente a função wavelet. Os termos deslocamento e escalonamento foram escolhidos intencionalmente, pois a literatura de wavelet frequentemente menciona o deslocamento e o escalonamento de uma wavelet mãe. Esperamos que a razão disso esteja clara agora. Vamos voltar agora nossa atenção para a equação da transformada de wavelet, repetida aqui.

Da esquerda para a direita, temos um rótulo para essa transformada no lado esquerdo da equação, com dois parâmetros que podemos definir. No lado direito, temos a integral do sinal f(t) multiplicada por 1/ (que é apenas um número), multiplicada pelo complexo conjugado da função wavelet deslocada e escalonada. A ideia de multiplicar duas funções não deve intimidar. Se você puder imaginar as duas funções, apenas imagine uma multiplicação ponto por ponto das duas. Isso não deve ser muito difícil quando ambas as funções possuem um suporte relativamente curto. Qualquer coisa multiplicada por zero é zero. Todos os valores antes do início do Chapéu Mexicano ou após o seu término serão iguais a zero, assim como será qualquer coisa multiplicada por ele. Uma vez que tenha visualizado a função que resulta da multiplicação de duas funções, imagine que você possa encontrar a área abaixo. É isso que a integral calcula. Em última análise, a equação da wavelet retorna um único número (possivelmente complexo) para s e u fornecidos. Esse número pode ser complexo se a wavelet ou o sinal original tiver valores complexos. O valor da transformada encontra-se no exame dos resultados para muitos valores de s e u.

9.5

Avaliação de Integrais

O cálculo numérico permite que aproximemos uma integral como um somatório [11]:

em que

Especificamos o número de intervalos a computar, n, e obtemos aproximações cada vez melhores à medida que n se torna maior. (Na prática, a definição da integral definida [11] consiste em permitir que n se aproxime de infinito.) Agora, uma preocupação prática é que a transformada de wavelet, conforme fornecida na Equação 9.4, possui como limites o infinito e o infinito negativo. Claramente, não podemos utilizar isso. Mas a função wavelet possui suporte finito, e portanto sabemos que existe uma faixa limitada. Qualquer coisa além dessa faixa será igual a zero e não contribuirá para a nossa soma. A maioria dos sinais do mundo real também possui faixa limitada. Saber o número de intervalos a computar pode ser um tanto complicado. Além disso, existem modos mais eficientes de computar numericamente uma integral. Porém, essas preocupações estão fora de nosso escopo. Vamos examinar a equação

Se considerarmos a = −10, b = 10 e n = 2000, veremos que a fração à direita (b − a)/n torna-se 20/2000, ou 0.01. Isso é o mesmo que o incremento para t utilizado nos exemplos anteriores. A variável i atua como

Weeks 009.indd 292

02/05/12 16:19

A Transformada Contínua de Wavelet

293

um índice. Portanto, xi começa em −10, possui um incremento de 0.01 e um valor máximo quando i = n − 1. Se aplicarmos esse valor para i na equação para xi, obteremos a + (n − 1)(b − a)/n. Ao aproximarmos n − 1 para n, simplificamos para a + (b − a) ou simplesmente b como o valor máximo. Assim, os valores xi servem ao mesmo propósito que t. A aproximação da integral reduz-se ao nosso incremento escolhido, multiplicado pela soma da função avaliada ao longo da faixa fornecida. A seguir apresentamos três exemplos de solução de integrais definidas ao longo de uma pequena faixa. Em seguida, comparamos os resultados com a solução analítica.

Exemplo 9.1

Quando integramos essa função, encontramos a solução a seguir.

Se calcularmos isso com o computador, estimaremos x2 + 3x + 4 com valores de x entre −10 e 10. O incremento é importante: quanto menor ele for, mais preciso será nosso cálculo. Mas isso também aumenta a solicitação de recursos computacionais (tempo de CPU e memória). O código se parece com este a seguir. As variáveis a e b contêm os limites −10 e 10, respectivamente. x = a:increment:(b-increment); f = x.*x + 3*x + 4; sum(f)*increment Podemos ajustar o incremento para verificar como a precisão varia. A saída para diversas execuções segue abaixo: Evaluating integral x^2 + 3x + 4 dx from -10.00 to Analytical answer is 746.67 Computed answer is 743.70 for increment 0.10000 Computed answer is 746.37 for increment 0.01000 Computed answer is 746.64 for increment 0.00100 Computed answer is 746.66 for increment 0.00010 Computed answer is 746.67 for increment 0.00001

10.00

À medida que o incremento diminui, o número de pontos em t aumenta. Para o incremento de tamanho 0.00001, há 2 milhões de pontos em t. Mas a resposta computada corresponde à analítica. Uma preocupação acerca desse processo é que podemos não dispor de uma resposta analítica para comparar com nosso valor computado. Essa questão está fora de nosso escopo aqui, mas um bom livro de análise numérica tratará do problema.

Weeks 009.indd 293

02/05/12 16:19

294

CAPÍTULO 9

Exemplo 9.2 Aqui, examinamos duas funções multiplicadas entre si.

A integração dessa função nos fornecerá esta solução:

O código principal utilizado para computar isso possui o mesmo formato simples de antes. Novamente, a é igual a −10 e b é igual a 10. Utilizaremos vários incrementos. x = a:increment:(b-increment); fg = 8*x.*x.*x + 3*x.*x; sum(fg)*increment A saída a seguir mostra a computação para diversos valores de incrementos. Evaluating integral 8 x^3 + 3 x^2 dx from -10.00 to 10.00 Analytical answer is 2000.00 Computed answer is 1200.10 for increment Computed answer is 1920.00 for increment Computed answer is 1992.00 for increment Computed answer is 1999.20 for increment Computed answer is 1999.92 for increment Computed answer is 1999.99 for increment

0.100000 0.010000 0.001000 0.000100 0.000010 0.000001

Para obtermos a precisão mostrada na linha final, tínhamos 20 milhões de valores para t. Cada valor possui tipo duplo (8 bytes), e portanto o computador utiliza 153 MB só para armazenar a lista t. E outro bloco de memória de mesmo tamanho armazena a lista f. Naturalmente, poderíamos utilizar um loop com uma variável escalar t como o índice, em vez de armazenar essas listas. Nesse caso, porém, estaríamos trocando uso de memória por tempo extra de processamento, presumindo que nosso ambiente computacional seja mais eficiente para computações com listas, tal como com o MATLAB. A questão aqui é que a quantidade de recursos computacionais torna-se excessivamente grande à medida que o incremento diminui, embora isso possa ser necessário. Claramente, o primeiro par de estimativas é insatisfatório. Se o incremento 0.01 produz resultados insatisfatórios para este exemplo, como saber se ele é bom o bastante para nossos objetivos com a função Chapéu Mexicano? Podemos tentar encontrar a resposta alterando o valor de nosso incremento. Se o resultado não mudar significativamente com um incremento menor (digamos, um décimo do atual), poderemos ficar seguros de que o incremento utilizado para encontrar o resultado funciona.

Weeks 009.indd 294

02/05/12 16:19

A Transformada Contínua de Wavelet

295

Exemplo 9.3 Aqui temos um exemplo mais complexo, mas só por causa da integração.

A integral à direita pode ser calculada como sin(x) em qualquer tabela de integrais apropriada. A integral à esquerda é mais complicada, mas funciona bem com integração por partes. Com essa técnica de integração, escolhemos as partes u e dv de uma integral difícil, calculamos v e du e encontramos uv e ∫ v du em seguida. Isso possivelmente tornará a solução do problema mais fácil.

Escolhemos dv = cos(x)dx e u = 4x. Em seguida, calculamos v = ∫ cos(x)dx = sin(x) e du = d(4x) = 4dx. Aplicamos esses valores na integração por partes para encontrar:

De volta à nossa equação original, encontramos a resposta analítica a seguir:

Podemos simplificar ainda mais lembrando que cos(␾) = cos(−␾) e sin(−␾) = −sin(␾):

Novamente, o código pertinente para encontrar essa solução numericamente parece-se com este: x = a:increment:(b-increment); fg = (4*x + 1).*cos(x); sum(fg)*increment Executamos o código e constatamos que os resultados correspondem às nossas expectativas.

Weeks 009.indd 295

02/05/12 16:19

296

CAPÍTULO 9

Evaluating integral 4x cos(x) from -10.00 to 10.00 Analytical answer is -1.09 Computed answer is 2.27 for Computed answer is -0.75 for Computed answer is -1.05 for Computed answer is -1.08 for Computed answer is -1.09 for

+ cos(x) dx

increment increment increment increment increment

0.10000 0.01000 0.00100 0.00010 0.00001

Com um incremento suficientemente pequeno, computamos com sucesso a integral ao longo da faixa −10 a 10. A seção a seguir utiliza .* para multiplicar duas listas ponto por ponto. Os exemplos nesta seção poderiam ter sido resolvidos com um código como este: t = a:increment:(b-increment); f = 2*t.*t; g = 4*t + 3/2; sum(f.*g)*increment Isso efetivamente recria o segundo exemplo e produz os mesmos resultados. Evaluating integral (2 x^2) (4 Analytical answer is 2000.00 Computed answer is 1200.10 for Computed answer is 1920.00 for Computed answer is 1992.00 for Computed answer is 1999.20 for Computed answer is 1999.92 for Computed answer is 1999.99 for

x + 3/2) dx increment increment increment increment increment increment

from -10.00 to

10.00

0.100000 0.010000 0.001000 0.000100 0.000010 0.000001

A título de exercício, você obteria os mesmos resultados no terceiro exemplo se definisse as funções f e g separadamente e calculasse sua multiplicação ponto por ponto?

A aproximação de uma integral com um somatório é importante para o cálculo da transformada contínua de wavelet com um computador. Não podemos calcular uma CWT verdadeira com um dispositivo digital, mas podemos encontrar uma boa aproximação. A seguir, examinaremos outra peça do quebracabeça: como multiplicar duas funções de modo a calcular sua integral.

9.6

Multiplicação de uma Função pelo Chapéu Mexicano

Apresentamos a seguir um exemplo de multiplicação de duas funções. Utilizamos o Chapéu Mexicano como uma função e uma função triângulo como a outra. A Figura 9.6 mostra a função Triângulo (parte superior), seguida da função Chapéu Mexicano (parte central) e a multiplicação das duas (parte inferior). A obtenção desse resultado não foi difícil. Uma vez que as funções tenham sido definidas pelo computador, simplesmente efetuamos uma multiplicação ponto por ponto. O código se parece com este: t = -10:0.01:10; f = triangle(t)/2; psi = mexicanhat(t); fpsi= f .* psi;

Weeks 009.indd 296

02/05/12 16:19

A Transformada Contínua de Wavelet

297

A função Triângulo

2 1 0 –10

–8

–6

–4

–2

0

2

4

6

8

10

6

8

10

6

8

10

A função Chapéu Mexicano

1 0.5 0 –0.5 –10

–8

–6

–4

–2

0

2

4

Multiplicação das duas funções 0 –0.2 –0.4 –0.5 –10 Figura 9.6

–8

–6

–4

–2

0

2

4

A função Triângulo, a função Chapéu Mexicano e a multiplicação das duas.

A função triangle foi incluída no site da LTC Editora. Ela não faz parte do MATLAB, mas uma função similar, triang, está disponível e pode ser modificada para fornecer os resultados que desejamos. Ou, caso seja preferível, salve a função que aparece abaixo com o nome de arquivo triangle.m. function out = triangle(t) out = ((t > 0) & (t small_t = -0.4:0.2:0.4 small_t = -0.4000

-0.2000

0

0.2000

0.4000

>> less_than_zero = (small_t < 0) less_than_zero = 1

1

0

0

0

Aqui geramos alguns valores de −0.4 a 0.4 e os utilizamos em seguida em uma operação lógica. Imagine que o computador examine cada valor de small_t da esquerda para a direita e anote se ele é inferior a zero. O resultado mostra que obtemos um sempre que a condição é verdadeira e zero sempre que ela é falsa. Na verdade não importa como a função triângulo é obtida, mas a multiplicação exibida na Figura 9.6 efetivamente utiliza a função triângulo que acabamos de definir. Naturalmente, o produto de duas funções depende de como elas estão alinhadas. Vamos tentar novamente, mas desta vez deslocando o Chapéu Mexicano de tal maneira que ambas as funções fiquem centralizadas em torno de 4 (veja a Figura 9.7). Note como o resultado se parece com o Chapéu Mexicano, porém é maior no centro e menor nos lados, tal como a função triângulo.

Weeks 009.indd 297

02/05/12 16:19

298

CAPÍTULO 9

A função Triângulo

2 1 0 –10

–8

–6

–4

–2

0

2

4

6

8

10

6

8

10

6

8

10

A função Chapéu Mexicano

1 0.5 0 –0.5 –10

–8

–6

–4

–2

0

2

4

Multiplicação das duas funções

2 1 0 –1 –10 Figura 9.7

–8

–6

–4

–2

0

2

4

A função Triângulo, a função Chapéu Mexicano (centralizada em 4) e a multiplicação das duas.

Um aspecto interessante para se pensar é no que representa a soma dos produtos de duas funções. Num certo sentido, ela nos informa o quanto são parecidas. Pense na multiplicação de uma função (digamos, o Chapéu Mexicano) por ela mesma. A soma resultante será máxima quando estiver alinhada corretamente. Isso não faz lembrar a correlação? Considere o código a seguir, extraído do programa cwt_example.m. count = 1; s = 1/2; increment = 0.01; t = 0:increment:(10-increment); f = mexicanhat(t-5); urange = 0:0.1:10; for u=urange % calcule uma saida CWT mexhat = mexicanhat((t-u)/s); fpsi = f .* mexhat; W(count) = sum(fpsi)*increment/sqrt(s); count = count + 1; end Ele calcula a função Chapéu Mexicano, centralizada em 5, e a armazena na variável f. Essa é a função que queremos analisar. No interior do loop, ele calcula a função Chapéu Mexicano novamente, só que deslocada pela variável u (que controla o loop) e escalonada pela variável s. Ele calcula a soma do produto dessas duas funções e a armazena em uma lista. A Figura 9.8 exibe um traçado da lista de somas. Note como o valor máximo aparece em 5 no eixo x, exatamente onde o sinal f foi centralizado. O programa cwt_example.m contém um pouco mais de código no interior do loop duplo, mostrado a seguir: % Trace o sinal, o chapeu mexicano e o produto figure(1); plot(t, f, 'k', t, mexhat, 'b', t, fpsi, 'r'); pause(0.1);

Weeks 009.indd 298

02/05/12 16:19

299

A Transformada Contínua de Wavelet

0.8

CWT da função Chapéu Mexicano, s = 1/2, u = 0, …, 10

0.6 0.4 0.2 0 –0.2 –0.4 Figura 9.8 A CWT Chapéu Mexicano da função Chapéu Mexicano, centralizada em 5.

–0.5 –10

–8

–6

–4

–2

0

2

4

6

8

10

Ele traça a função f juntamente com o Chapéu Mexicano calculado no loop, bem como o produto dos dois. Com a função pause, ele gera uma animação simples com o Chapéu Mexicano deslizando através do sinal original f. Utilizamos um incremento de 0.01 no programa cwt_example.m. Simplesmente executamos o programa, copiamos os resultados para uma nova variável, mudamos o incremento, reexecutamos o programa e assim comparamos os resultados anteriores com os novos. Eis como isso se parece: >> cwt_example >> W_01 = W; (O valor do incremento seria alterado nesse ponto, na linha increment = 0.01; para increment = 0.0001;. Certifique-se de salvar o programa antes de executá-lo novamente.) >> cwt_example >> diff = sum(abs(W - W_01)); >> disp(sprintf('diferenca = %10.9f',diff)) diferenca = 0.000008176 Visto que a utilização de 100 vezes mais amostras produz uma diferença tão pequena, concluímos que o primeiro valor de incremento funciona bem. Lembre que, uma vez que a multiplicação de duas funções aconteça, somamos todos os valores para calcular a integral. Na prática, devemos escalonar a soma — caso contrário obteremos resultados enganosos. Pense dessa maneira: suponha que tenhamos um retângulo com uma borda ao longo do eixo x e altura 1. Coletamos 10 amostras dele e somamos todas. A soma seria 10. Mas, se alguém coletasse 25 amostras do mesmo retângulo, poderia concluir que a soma (representando a área) é 25! Ambas as somas devem ser as mesmas e serão caso estejam apropriadamente escalonadas. Outro aspecto a se ter em mente é a faixa limitada dessas funções de exemplo. Somente examinamos os valores para um pequeno intervalo, tal como entre −10 e +10, pois tais funções de exemplo são iguais a zero fora dessa faixa.

9.7

Exemplo da CWT

Para mostrarmos um exemplo da CWT, precisamos de uma função simples, porém manejável. É tentador utilizar uma constante ou talvez uma onda quadrada, mas isso não nos leva a um exemplo manejável. Vamos examinar essa questão. Considere que nossa função f seja a constante 3. f=3

Weeks 009.indd 299

02/05/12 16:19

300

CAPÍTULO 9

O cálculo da wavelet contínua significa que estimamos a integral a seguir.

Agora, uma das propriedades de uma função wavelet é que ela possui área zero (veja a Equação 9.2). Isso significa que a integral anterior resulta em zero, de modo que nosso resultado W(s, u) também é sempre zero. Portanto, não podemos utilizar f = 3 como uma função de exemplo manejável. Curiosamente, a função cwt do MATLAB nos fornece saídas diferentes de zero para uma função como essa. Considere o código a seguir. increment = 0.01; t = 0:increment:(10-increment); f(1:length(t)) = 3; Wmat = cwt(f,1:50,'mexh'); plot(t, Wmat(25,:)); Definimos f como a constante de valor 3. Utilizamos a função cwt e observamos que os resultados são iguais a zero, exceto nas bordas do sinal. A Figura 9.9 exibe a saída do código anterior, que claramente contém valores diferentes de zero nas bordas. Em parte isso advém do valor s igual a 25 que escolhemos para o traçado. Nos valores baixos para s, o efeito não é visto tão facilmente. Não é possível determinar por que isso ocorre sem examinar o código que o MATLAB utiliza. Uma possibilidade provável é a ocorrência de um efeito de borda devido ao fato de o sinal f ser definido em uma faixa finita. Nossa intenção é que a função seja igual a 3 em toda parte, mas não dispomos de uma forma apropriada de transmitir essa ideia para o computador. Ou seja, qual seria o valor de f antes do primeiro valor, ou após o último? Se o considerarmos igual a 0 (tal como um programa o faria), teremos

CWT de uma constante, onde s = 25

8 7 6 5 4 3 2 1 0 –1

0

1

2

3

4

5

6

7

8

9

10

Figura 9.9 A CWT de uma função constante.

Weeks 009.indd 300

02/05/12 16:19

A Transformada Contínua de Wavelet

301

uma borda de transição de 0 a 3 no início e no final de f. Essa transição (uma mudança rápida) aparece na saída da função cwt.

9.7.1

Exemplo Analítico da CWT em uma Função

Suponha que desejemos calcular a DWT da função

Escolhemos essa função deliberadamente, para facilitar nossa análise a seguir. Para calcularmos a CWT com a wavelet Chapéu Mexicano, encontramos:

Agora substituímos ␺ pela definição da função Chapéu Mexicano.

Isso é difícil de visualizar, e portanto vamos selecionar valores específicos para s e u para simplificar as coisas.

A constante C aparece uma vez que tenhamos uma integral indefinida. Se estimarmos W(1, 0) em t = 10, computaremos −323.33, mais a constante. Como isso se compara a uma resposta calculada? Precisamos de algum código para realizar isso. Primeiramente, vamos definir nossas variáveis e a função f. increment = 0.01; t = 0:increment:(10-increment); f = exp(t.*t/2); Agora encontramos ␺1,0 e a armazenamos na variável mexhat. Em seguida a multiplicamos pela função f e calculamos a integral. mexhat = mexicanhat((t-0)/1); fpsi = f .* mexhat; W_1_0 = sum(fpsi)/sqrt(1) * increment;

Weeks 009.indd 301

02/05/12 16:19

302

CAPÍTULO 9

Ao fazermos isso, calculamos o valor para W_1_0 como −322.83. As respostas são próximas, mas não exatas. Vamos mudar o valor do incremento (isto é, consideremos increment = 0.0001;) e tentar novamente. increment = 0.0001; t = 0:increment:(10-increment); f = exp(t.*t/2); mexhat = mexicanhat((t-0)/1); fpsi = f .* mexhat; Executamos esse código e agora inserimos a última linha sem o ponto e vírgula, de modo a visualizar o resultado. >> W_1_0 = sum(fpsi)/sqrt(1) * increment W_1_0 = -323.3283 Vemos que o resultado é muito mais próximo de nossa resposta analítica e concluímos que os dois métodos funcionam para esse exemplo.

9.7.2

Exemplo Analítico da CWT em uma Função Impulso

As funções impulso produzem exemplos interessantes. Considere uma função onde f(2) = 1, mas f(t) = 0 para todos os demais valores de t. Quando calculamos a transformada contínua de wavelet na função, temos a seguinte análise:

Uma coisa interessante acontece quando consideramos a integral. Visto que f(t) é zero em todos os pontos exceto um claramente definido, a integral também é zero quando t = / 2. Pense nisso desta maneira: decompomos a integral em três partes, como a seguir. Considere que ⑀ seja um valor muito pequeno, como 0.0000001.

Por definição, nossa função é zero para todos os valores t inferiores a 2 e maiores que 2. Com isso em mente, reescrevemos W f(u, s) aqui:

Assim, podemos simplificá-la:

Weeks 009.indd 302

02/05/12 16:19

A Transformada Contínua de Wavelet

303

Tudo que resta é a função ␺. Dadas nossas escolhas para s e u, podemos calcular facilmente o número que ela retorna. Assim, o que obtemos quando calculamos a CWT? Obtemos uma versão muito estreita do Chapéu Mexicano, exatamente em torno de t = 2. Todos os demais valores da CWT são iguais a zero. Isso é significativo, pois o impulso muda rapidamente em um ponto; a transformada de wavelet acentua esse ponto.

9.7.3

A CWT em uma Função Quadrado

Em seguida, consideraremos uma função quadrado. Nós a definimos como um ao longo do intervalo t = 2 a t = 8 e zero nos demais pontos. O código a seguir define essa função utilizando a expressão booleana (t>=2) & (t=2) & (t x = wavrecord(16000, 8000, 'double'); ??? Error using ==> wavrecord WAVRECORD is only for use with Windows 95/98/NT machines. Uma solução fácil consiste em utilizar um software externo ao MATLAB para gravar dados. Uma vez que estejam salvos na forma de um arquivo, podemos utilizar o MATLAB para lê-los. Existe ainda outra possibilidade, que discutiremos em breve. Suponha que tenhamos resolvido a questão da gravação e que nossos dados sonoros estejam em x. A seguir, podemos querer reproduzi-los. 315

Weeks 010.indd 315

20/06/12 10:02

316

CAPÍTULO 10

>> wavplay(x) ??? Error using ==> wavplay WAVPLAY is only for use with Windows 95/98/NT machines. O erro apresentado (“WAVPLAY só pode ser utilizado com máquinas Windows 95/98/NT”) pode ser resolvido com o comando sound. Ele funciona exatamente como o comando wavplay, mas é suportado pelo MATLAB tanto em OS X quanto em Unix/Linux. Como ele também funciona em computadores rodando sistemas operacionais da Microsoft, utilize sound em vez de wavplay, por uma questão de compatibilidade. >> sound(x) >> sound(x, 8000); Tal como mostra o exemplo, ele também requer uma taxa de amostragem como o segundo parâmetro. O que aconteceria se você não fosse capaz de executar os exemplos anteriores? Podemos criar um som de exemplo com uma senoide e reproduzi-lo em seguida, como no exemplo a seguir. Ele deve funcionar em qualquer computador equipado com o MATLAB e uma placa de som funcional. % crie um som de exemplo t = 0:0.0001:3; x = 0.9*cos(2*pi*440*t); % reproduza-o sound(x, 8000); Consideremos que ainda dispomos de x e vamos gravá-lo em um arquivo. Você reconhecerá 8000 como a frequência de amostragem, e example_sound.wav é o nome do arquivo a ser criado. O outro parâmetro, 16, fornece o número de bits por amostra. Embora a função wavwrite utilize 16 como padrão, existem outras possibilidades. wavwrite(x, 8000, 16, 'example_sound.wav'); Uma vez que tenhamos criado o arquivo do som de exemplo, desejaremos lê-lo ou reproduzi-lo mais cedo ou mais tarde. Assim como a taxa de amostragem e o número de bits são gravados como parte do arquivo, ambos são retornados quando os lemos como o segundo e o terceiro valores, respectivamente. [x2, fs, b] = wavread('example_sound.wav'); Uma inspeção dos valores para fs e b revela que estes são os mesmos que os dos parâmetros que fornecemos ao comando wavwrite. Agora vamos criar um novo som a partir daquele que acabamos de ler do disco. Com o que se parece o som mysound? Por quê? Este código demonstra o acesso aleatório, uma vantagem da mídia digital sobre a analógica. mysound = x2(length(x2):-1:1); % reproduzi-lo sound(mysound, fs); Vimos alguns exemplos que trabalham com o formato de áudio .wav, embora existam outras possibilidades. Um programa pode ler e gravar arquivos sonoros no formato de áudio .au da Sun,1 com os comandos auread e auwrite, respectivamente. Outros formatos de arquivos sonoros podem ser lidos, com uma pequena ajuda de uma pesquisa na Internet. Você pode encontrar o código disponível para ler o formato de arquivo que desejar, ou, com determinação e documentação suficientes, escrever o seu próprio código.

1

Weeks 010.indd 316

Sun Microsystems, atualmente uma subsidiária da Oracle Corporation. (N.T.)

20/06/12 10:02

Aplicações

317

Outra forma de gravação emprega o comando audiorecorder, recentemente incorporado ao MATLAB (incluso na Versão 7, mas não na Versão 6). Ele utiliza objetos especiais de gravação de áudio, de modo que nossa implementação funcional demandará uma explicação adicional. Vamos ver como será o código para nosso primeiro exemplo de gravação/reprodução. Primeiramente, o código a seguir define um objeto audiorecorder que rotulamos como x_object. Em seguida, chamamos a função record, que grava os dados para um objeto. Aqui nós a utilizamos com um microfone, mas ela poderia ser utilizada para outras comunicações, tais como aquelas com um dispositivo conectado por meio de uma porta serial. O segundo parâmetro no comando record especifica o número de segundos da gravação. Seguiremos com um comando pause, para dar tempo ao computador de fazer a gravação (2 segundos para gravar, mais uma margem de segurança de .2 segundo). Se tivéssemos mais alguma coisa a ser feita, alocaríamos o código correspondente aqui. Além disso, existe uma função semelhante, recordblocking, que não retorna até que a gravação esteja concluída. Por fim, o código a seguir utiliza o comando play, que trabalha com objetos audiorecorder tal como sound ou wavplay trabalhava com os dados nos exemplos anteriores. Se não utilizarmos o sinal de ponto e vírgula ao final do comando play, informações sobre o objeto serão exibidas. x_object = audiorecorder(44100, 8, 1); record(x_object, 2); pause(2.2); play(x_object) Embora possamos querer gravar a uma taxa de amostragem mais baixa, talvez ela não seja suportada. O autor descobriu que, se a função record for chamada para um objeto audioplayer de 8000 amostras/segundo, uma mensagem de alerta “unsupported audio format” (formato de áudio não suportado) surgirá, enquanto uma chamada semelhante para a função recordblocking não gera retorno algum! A diferença entre as duas chamadas deve ser simplesmente o momento em que o controle retorna ao usuário: record retorna o controle imediatamente (prosseguindo seu trabalho em segundo plano), enquanto recordblocking retorna o controle após o tempo de gravação ter transcorrido. Podemos utilizar a função sound com o objeto audioplayer? Sim, embora precisemos primeiramente obter os dados em um formato que a função sound possa utilizar. Em outras palavras, precisamos dos dados brutos de som, e não do refinado encapsulamento do objeto. A função getaudiodata provê tal comodidade, conforme vemos aqui: % converter os dados em uma lista de duplos x = getaudiodata(x_object, 'double'); % reproduzi-los sound(x, 44100); Em resumo, dos comandos utilizados nesta seção, vimos que wavrecord e wavplay não são suportados em todas as plataformas. Para contornar essa incompatibilidade, outro software (além do MATLAB) pode ser empregado para gravar o som, e o comando sound pode ser utilizado no lugar de wavplay. O comando audiorecorder e funções correlatas também permitem ao usuário trabalhar com som. Vale a pena explorar tais comandos, embora eles não estejam implementados em versões mais antigas do MATLAB.

10.2

Exemplos de Aplicação com Imagens

Trabalhar com imagens pode ser gratificante. Os resultados são prontamente visíveis, em uma forma concreta. O MATLAB disponibiliza funções que facilitam o processamento de imagem. Os exemplos nesta seção têm o intuito de proporcionar ao leitor uma rápida compreensão do que o MATLAB é capaz de fazer. O código a seguir desenha um pequeno retângulo branco em um plano de fundo preto. O comando imwrite salva a imagem no disco, no formato TIFF. % Crie uma matriz 128x128 de valores zero x = zeros(128, 128); % Desenhe um retangulo branco solido x(20:30, 20:40) = 255;

Weeks 010.indd 317

20/06/12 10:02

318

CAPÍTULO 10

% Salve-o como o arquivo “whiterect.tiff“ imwrite(x, 'whiterect.tiff', 'TIFF'); % Mostre-o na tela imshow(x); Em seguida, leremos o arquivo do disco e comutaremos as linhas e colunas da imagem. Naturalmente, o comando imread não é necessário caso a imagem ainda esteja na memória. % primeiramente, leia a imagem x = imread('whiterect.tiff'); % troque linhas pelas colunas y = x.'; imshow(y); Se a imagem não aparecer quando o código anterior for executado, a janela para a imagem provavelmente estará apenas escondida. Uma chamada para figure(1) trará a janela para a frente, considerando-se de que se trata da primeira figura. Também podemos comutar as linhas e colunas de uma imagem mais complexa. A imagem “dog on porch”2 encontra-se disponível no CD-ROM e será utilizada para os exemplos a seguir. Repetiremos o exemplo anterior, apenas de forma um pouco mais concisa. Em primeiro lugar, vamos simplesmente carregar a imagem e exibi-la. dog = imread('dog256x256.gif'); imshow(dog); Agora vamos exibi-la com as linhas e colunas comutadas, como uma segunda figura. figure(2); imshow(dog.'); Como se pode constatar a partir das duas imagens, a transposição de uma imagem não equivale à rotação da mesma. Para uma rotação no sentido horário, precisamos exibir a primeira coluna, de baixo para cima, como a primeira linha. Em seguida, repetimos isso para a coluna seguinte, depois para a seguinte etc. O código a seguir faz precisamente isso e exibe a imagem girada. for r=1:256 clockwise_dog(r, 1:256) = dog(256:-1:1, r); end figure(3); imshow(clockwise_dog); Isso funciona porque por acaso sabemos com antecedência quais são as dimensões da imagem. O que aconteceria se não soubéssemos? Vejamos o que acontece quando generalizamos esse código. Primeiramente, criaremos uma nova imagem como um subconjunto da original. Enquanto estivermos nela, fecharemos todas as figuras abertas e exibiremos a nova imagem. dog2 = dog(10:100, 20:200); close all imshow(dog2); Percebemos que somente a parte superior da imagem é copiada para a nova imagem. Agora nós a giraremos, como se não conhecêssemos as dimensões.

2

Weeks 010.indd 318

“Cão na varanda”. (N.T.)

20/06/12 10:02

Aplicações

319

[MAX_ROWS, MAX_COLS] = size(dog2); for r=1:MAX_COLS clockwise_dog2(r, 1:MAX_ROWS) = dog2(MAX_ROWS:-1:1, r); end figure(2); imshow(clockwise_dog2); A execução do código anterior mostra a nova imagem, girada no sentido horário, conforme o esperado. Nesta seção, examinamos alguns exemplos básicos de aplicação com imagens no ambiente de programação MATLAB. Em seguida, veremos uma aplicação de processamento de imagem mais complexa.

10.3

Compressão de uma Foto

As pessoas utilizam imagens de todos os tamanhos. Por exemplo, um turista pode tirar fotos utilizando uma câmera digital com resolução de múltiplos megapixels e carregar a imagem para um website. Em seguida, ele pode desejar uma pequena versão do tamanho de uma miniatura (thumbnail) como um link para a foto carregada. A figura carregada não deve ter o tamanho original, ou levará muito tempo para que um visitante do website a descarregue. Um meio-termo satisfatório incluiria três versões da mesma foto: a original, uma versão de tamanho médio e uma versão em miniatura. Se a câmera digital nos fornece uma foto original grande, como podemos gerar as menores? Podemos utilizar a ideia da função de escalonamento da DWT para criar uma imagem menor a partir de uma maior. Faremos uma aproximação com metade do número de linhas e metade do número de colunas, produzindo uma imagem resultante com um quarto do tamanho original. Uma vez que a transformada de wavelet pode utilizar a aproximação para oitavas subsequentes, podemos chamar recursivamente uma função de aproximação até que o tamanho alcance ou ultrapasse um limite definido. Suponha que utilizemos a foto DSCF0980.JPG, disponível no CD-ROM. Podemos carregá-la em uma matriz com o MATLAB, tal como mostra o código a seguir. >> x = imread('DSCF0980.JPG'); >> size(x) ans = 1728

2304

3

>> disp(1728*2304 / (1024*1024)) 3.7969 O tamanho da foto é de 1728 × 2304, ou 3.8 megapixels, maior do que a maioria dos monitores computacionais é capaz de exibir. Podemos tentar exibir a imagem na tela, da seguinte maneira: >> imshow(x) Warning: Image is too big to fit on screen; displaying at 50% > In imuitools/private/initSize at 73 In imshow at 262 Note que o MATLAB escalona automaticamente a foto para nós, pois neste caso a imagem ultrapassa a resolução do monitor.

10.3.1

Uma Abordagem de Baixo para Cima

Vamos escrever um programa que produz imagens de tamanho médio e pequeno. Na verdade, criaremos duas funções e um programa. Primeiramente, criaremos uma função que comprime uma imagem para um

Weeks 010.indd 319

20/06/12 10:02

320

CAPÍTULO 10

quarto de seu tamanho. Em seguida, chamaremos essa função repetidamente a partir de outra função, até que o tamanho resultante corresponda ao nosso critério. Poderemos então criar um programa que chame aquela função repetidas vezes com imagens diferentes. Um modo fácil de reduzir uma imagem para um quarto de seu tamanho consiste em calcular a média de um bloco 2 × 2 e, em seguida, armazenar o resultado como um único valor. Repetimos então esse procedimento para cada bloco 2 × 2 na imagem de entrada. Existem, contudo, algumas complicações com as quais precisamos lidar. Imagens coloridas possuem três valores por pixel: um componente vermelho, um componente verde e um componente azul. Você pode imaginar como eles se combinam para compor a cor resultante, tal como a mistura de tintas coloridas. Assim, nossa função precisará executar o cálculo de média 2 × 2 em cada componente de cor separadamente. Outro desafio a ser vencido refere-se ao tipo de dado. Cada pixel possui valores inteiros de 8 bits sem sinal para cada componente de cor, de modo que a utilização de um tipo double não faz sentido. No entanto, não podemos manipular valores de 8 bits matematicamente da forma que poderíamos esperar. O código a seguir, por exemplo, nos fornece um resultado que você pode achar surpreendente. >> a = uint8(200); >> a+a ans = 255 Um tipo de dado de 8 bits como o uint8 não pode armazenar valores maiores que 255. Quando calculamos o resultado do uint8 para 200 + 200, o computador retorna o maior valor que se ajusta àquele tipo de dado. Portanto, a função deve converter de uint8 em double, efetuar o cálculo e converter o resultado de volta em uint8. Também devemos lidar com a indexação entre o conteúdo original e os resultados com a média calculada. Para facilitar as coisas, teremos duas matrizes: uma para a entrada e uma para a saída. Indexaremos as linhas e colunas com um loop for, pulando cada segunda linha e coluna (uma sim, outra não). Os índices são, portanto, relativos à entrada. Para encontrarmos os índices apropriados para a saída, somamos um e dividimos por dois. Assim como em uma divisão inteira, isso garante que nossos índices sempre possuam valores um ou maiores, mas que correspondam à metade dos índices da imagem de entrada. Por uma questão de eficiência, inicializaremos a imagem de saída com o comando zeros, reservando a memória necessária. O código será como este a seguir. Mostramos apenas o cálculo de média para o componente vermelho, mas o processamento para os componentes verde e azul seguem imediatamente depois. Note que somamos um aos valores r e c, em um par de combinações, para acessar os elementos que nossos índices pulariam. function [newimage] = shrink_image(original) [MAXROWS, MAXCOLS, DEPTH] = size(original); for r = 1:2:MAXROWS-1 for c = 1:2:MAXCOLS-1 % Red pixels summ = double(original(r, c, 1)) ... + double(original(r+1, c, 1)) ... + double(original(r, c+1, 1)) ... + double(original(r+1, c+1, 1)); newimage((r+1)/2, (c+1)/2, 1) = uint8(summ /4); % ... Green and Blue follow end end Dentro dos loops for aninhados, designamos a variável summ para que seja o resultado da soma dos componentes vermelhos de quatro pixels. Ao utilizarmos (r, c) como o ponto de partida, somamos o valor vermelho do pixel abaixo (r + 1, c), aquele à direita (r, c + 1) e aquele abaixo à direita (r + 1, c + 1).

Weeks 010.indd 320

20/06/12 10:02

Aplicações

321

Em seguida, armazenamos a média do resultado (summ/4) na matriz newimage. Repetimos esse procedimento para todas as linhas e colunas (valores r e c). Desta forma, prosseguimos por toda a imagem de entrada. Após a conclusão, a função retorna a matriz newimage. Para a função completa, examine o arquivo shrink_image.m. Essa abordagem possui a vantagem de ser simples e direta. Observe que modos mais eficientes e melhores de se fazer isso podem ser encontrados. A título de exercício, como você poderia encontrar um newimage equivalente utilizando multiplicação matricial? Além disso, como você poderia alterar a função para criar uma imagem de saída escalonada por um valor diferente, tal como um pixel resultante para cada sub-bloco 2 × 3?

10.3.2

Um Nível Adicional de Simplificação

Para tornar este processo mais amigável, criamos uma segunda função que chamará repetidamente a função shrink_image. Esperamos que o usuário especifique dois nomes de arquivo, a imagem original a ser lida e a nova imagem (menor) a ser criada. Também precisamos de um valor limite, um número que nos informe o quanto pequeno é pequeno o suficiente. Nós a chamaremos CutOff, e esperamos que o usuário a especifique também. Em seguida, leremos a imagem e encontraremos suas dimensões. Depois, enquanto o número de linhas ultrapassar o valor CutOff, reduziremos a imagem repetidamente para um quarto de seu tamanho. Por fim, gravaremos os dados da nova imagem no arquivo de saída cujo nome foi especificado. Agora que dispomos de um algoritmo com o qual trabalhar, criar tal programa será fácil. A maioria das etapas pode ser cumprida com poucas linhas de código. Aqui apresentamos a parte central do algoritmo, procedente da função thumb.m (inclusa no CD-ROM). while (ROWS > CutOff) disp(sprintf(' Shrinking image from %d x %d ...', ... ROWS, COLS)); x = shrink_image(x); [ROWS, COLS, DEPTH] = size(x); end Primeiramente, note que a compressão só ocorre quando há mais linhas do que o valor CutOff, considerando-se que a imagem possua um número razoável de colunas. Por exemplo, uma imagem com somente uma coluna não funcionaria. Portanto, se a imagem original já possuir menos linhas, a função thumb.m simplesmente gerará uma cópia da imagem ao gravá-la na imagem de saída. Para comprimirmos a matriz da imagem (x), chamamos a função externa de shrink_image. O CD-ROM contém uma cópia dessa função. Por fim, observe que devemos calcular o novo tamanho da matriz em cada passagem pelo loop.

10.3.3

Processamento em Lote

Com a função thumb chamando a função shrink_image, podemos produzir uma versão menor de uma imagem com uma única chamada de função para ela. Embora isso possa ser ineficiente em termos de cálculos computacionais, o modo mais fácil de obter uma imagem de tamanho médio e pequeno a partir da mesma imagem de grandes dimensões consiste em chamar a função thumb duas vezes. Sim, poderíamos gerar a imagem média primeiro e, em seguida, gerar a pequena a partir dela. Em vez disso, encontramos as imagens média e pequena, ambas a partir da original, tal como faria um usuário que não soubesse como a função thumb trabalha. Aqui vemos um programa muito simples chamado run_thumb.m. thumb('DSCF0980.JPG', 'DSCF0980_med.JPG', 600); thumb('DSCF0980.JPG', 'DSCF0980_sm.JPG', 400); A função thumb não requer nossa atenção uma vez que a chamemos. Isso a torna uma boa candidata ao processamento em lote. O programa run_thumb.m faz precisamente isso: ele chama a função thumb com diferentes parâmetros. Embora isso provavelmente não seja impressionante em si mesmo, podemos acres-

Weeks 010.indd 321

20/06/12 10:02

322

CAPÍTULO 10

centar facilmente quantas linhas desejarmos, talvez criando imagens de tamanho médio e pequeno a partir de uma lista inteira de fotos digitais. As funções fornecem alguma informação, que podemos transcrever em um arquivo de registro (log) para inspeção posterior, caso algo saia errado. As chamadas de função definem que a imagem média seja inferior a 600 linhas e a imagem pequena seja inferior a 400 linhas. Os tamanhos reais para este exemplo são 432 × 576 para a imagem média e 216 × 288 para a imagem pequena.

10.3.4

Utilização de Imagens Menores em uma Página Web

De posse das novas imagens de tamanho médio e pequeno, podemos utilizá-las em uma página web. O código a seguir especifica o código HTML, utilizando a menor imagem como um link para a de tamanho médio.

Click on the picture to see a larger version.







As primeiras duas linhas e a última linha são necessárias para que isso seja uma página web. Sinta-se à vontade para alterar o texto da terceira linha (“Clique na foto para uma versão maior.”). A quarta linha define uma referência (hyperlink) para outro arquivo. A parte img src instrui o carregamento da imagem DSCF0980_sm.JPG e a exibe no navegador do usuário. Uma vez que ela possui os tags de início e fim para um link em torno dela, será nisso que o usuário clicará para acessar o conteúdo em DSCF0980_med.JPG. Em outras palavras, a imagem pequena torna-se um link e aponta para a imagem de tamanho médio. Este exemplo pode ser encontrado on-line em http://carmaux.cs.gsu.edu/test_image.html. Além disso, você poderia salvá-la como um arquivo para que seu navegador de Internet a carregue diretamente por meio do recurso open-file (abrir arquivo).

10.4

Execução da Transformada Discreta de Wavelet Bidimensional em uma Imagem

A maioria dos textos sobre a DWT inclui uma imagem como aquela exibida na Figura 10.1. Ela mostra que a aproximação é simplesmente aquela e que os três detalhes incluem as bordas horizontal, vertical e diagonal. É muito difícil gerar uma imagem como esta?

Figura 10.1 DWT bidimensional em uma imagem.

Weeks 010.indd 322

20/06/12 10:02

Aplicações

323

Primeiramente, carregaremos a imagem e a exibiremos. Depois, executaremos a transformada discreta de wavelet (DWT) nesta imagem. Em seguida, tornaremos os resultados apresentáveis. O programa make_ image coleta uma matriz de entrada e retorna algo que podemos exibir. Uma vez que executemos a DWT, os resultados estão em ponto flutuante, poderiam ser negativos e certamente maiores que 255 (levando o valor de pixel a exceder a escala de tons de cinza). Assim, precisamos mapear os valores para a escala de cinza. Eis a seguir o programa make_image.m, necessário para a formação da imagem. Ele calcula a faixa de valores dentro de uma matriz, escalona-os para a faixa de 0 a 255 e os converte em seguida em valores inteiros uint8, de 8 bits sem sinal. Em outras palavras, dada uma matriz bidimensional, ele escalona todos os elementos para valores em escala de cinza. % make_imagem.m % Tome uma matriz bidimensional e retorne-a como uma imagem % (valores uint8, de 0 a 255). % function imX = make_image(x); minX = min(min(x)); maxX = max(max(x)); scale = maxX - minX; imX = uint8(floor((x - minX)*255/scale)); Observe que utilizamos min duas vezes, pois min(x) retorna uma linha inteira com os valores mínimos das colunas. Da mesma forma, a função max retorna uma lista quando fornecida uma matriz bidimensional. Podemos verificar os valores mínimo e máximo retornados pelo programa make_image fornecendolhe pequenas matrizes de exemplo e conferindo os resultados. Começaremos pelo mais fácil e os tornaremos um pouco mais complexos a cada vez. >> make_image([0, 1; 6, 512]) ans = 0 2

0 255

A “imagem” de exemplo é simplesmente uma matriz com duas linhas e duas colunas. Visto que os quatro valores de entrada do exemplo variam de 0 a 512 e deveriam em vez disso variar de 0 a 255, vemos que a função make_image mapeia tanto 0 quanto 1 para 0, depois mapeia 6 para 2 e 512 para 255. Do mesmo modo, vemos que ela escalona cada uma das pequenas matrizes de exemplo a seguir para valores entre 0 e 255. >> make_image([0, 1; -6, 512]) ans = 2 0

3 255

>> make_image([ -255, 1; -6, 255]) ans = 0 124

Weeks 010.indd 323

128 255

20/06/12 10:02

324

CAPÍTULO 10

>> make_image([ -255, 1; -6, 255]) ans = 0 124

128 255

>> make_image([ 100, 101; 106, 512]) ans = 0 3

0 255

A título de exercício, gere uma lista aleatória e verifique se o programa make_image sempre retorna resultados entre 0 e 255. Além disso, experimente-o com valores de ponto flutuante. O que aconteceria se valores complexos fossem passados para esse programa? Como ele poderia ser alterado para retornar somente inteiros reais?

10.4.1

DWT Bidimensional de uma Imagem em Tons de Cinza

Agora dispomos de nosso programa para mostrar os efeitos da DWT bidimensional em uma imagem. Primeiramente, carregaremos a imagem e a exibiremos na tela. % carregue a imagem x = imread('dog256x256.gif'); % mostre o original figure(1); imshow(x); title('original image'); Em seguida, executaremos a Transformada Discreta de Wavelet bidimensional na imagem. Aqui utilizamos a wavelet db16 de Daubechies, embora pudéssemos utilizar qualquer wavelet discreta que desejássemos. [A, B, C, D] = dwt2(double(x), 'db16'); Para tornar visualizáveis as diferentes subimagens, precisamos mapear seus valores para valores de pixel em escala de cinza. Utilizaremos o programa make_image da seção anterior. imA imB imC imD

= = = =

make_image(A); make_image(B); make_image(C); make_image(D);

Precisamos saber agora qual é o tamanho das subimagens. Podemos considerar que elas possuem o mesmo tamanho e criar uma grande matriz de imagem (superx) baseada no tamanho da subimagem imA. A função zeros apenas retorna uma matriz preenchida com zeros, tendo sido fornecidas as dimensões. Neste caso, os valores da matriz são todos do tipo uint8. [MAXA_row, MAXA_col] = size(imA); % As dimensoes devem ser todas iguais. %[MAXB_row, MAXB_col] = size(imB); %[MAXC_row, MAXC_col] = size(imC); %[MAXD_row, MAXD_col] = size(imD); superx = zeros(MAXA_row*2, MAXA_col*2, 'uint8');

Weeks 010.indd 324

20/06/12 10:02

Aplicações

325

Agora podemos alocar as subimagens nos quatro cantos da matriz superx. % coloque A no canto superior esquerdo superx(1:MAXA_row, 1:MAXA_col) = imA; % coloque B no canto superior direito superx(1:MAXA_row, MAXA_col+1:MAXA_col*2) = imB; % coloque C no canto inferior esquerdo superx(MAXA_row+1:MAXA_row*2, 1:MAXA_col) = imC; % coloque D no canto inferior direito superx(MAXA_row+1:MAXA_row*2, MAXA_col+1:MAXA_col*2) = imD; Por fim, exibimos a matriz superx como uma imagem. figure(2); imshow(superx); title('Apos uma oitava da DWT'); Ao executarmos esse código, veremos uma imagem como a da Figura 10.1. Isso funciona bem para imagens em tons de cinza, e expandiremos esse programa para imagens em cores na próxima seção.

10.4.2

DWT Bidimensional de uma Imagem em Cores

Podemos também executar a DWT bidimensional de uma imagem em cores. Um modo de lidar com isso consiste em tratar os três componentes de cor (vermelho, verde, azul) como sinais separados. Adotamos essa abordagem com o código a seguir. Note que convertemos os valores de pixel de uint8 em double quando os passamos para a função dwt. Trata-se de uma etapa necessária, pois a função dwt manipula esses valores matematicamente. % Execute a DWT de uma imagem em cores % primeiramente, carregue e exiba a imagem x = imread('colorimage.jpg'); figure(1); imshow(x); title('original image'); % Execute a DWT bidimensional no componente vermelho [Ar, Br, Cr, Dr] = dwt2(double(x(:,:,1)), 'db16'); % Execute a DWT bidimensional no componente verde [Ag, Bg, Cg, Dg] = dwt2(double(x(:,:,2)), 'db16'); % Execute a DWT bidimensional no componente azul [Ab, Bb, Cb, Db] = dwt2(double(x(:,:,3)), 'db16'); Para mostrarmos os resultados, devemos primeiramente mapear os valores para a faixa de 0 a 255. Fazemos isso para cada componente de cor separadamente.

Weeks 010.indd 325

% Crie imAr = imBr = imCr = imDr =

imagens a partir dos resultados da DWT para vermelho make_image(Ar); make_image(Br); make_image(Cr); make_image(Dr);

% Crie imAg = imBg = imCg = imDg =

imagens a partir dos resultados da DWT para verde make_image(Ag); make_image(Bg); make_image(Cg); make_image(Dg);

20/06/12 10:02

326

CAPÍTULO 10

% Crie imAb = imBb = imCb = imDb =

imagens a partir dos resultados da DWT para azul make_image(Ab); make_image(Bb); make_image(Cb); make_image(Db);

Em seguida, tornamos uma matriz suficientemente grande para conter todas as quatro subimagens. As dimensões para as subimagens devem ser todas iguais, de modo que baseamos o tamanho de nossa matriz grande na aproximação. Não utilizamos as dimensões da imagem original para isso, pois o comando dwt pode retornar valores extras de acordo com o limite. Essa matriz deve ser tridimensional, com a terceira dimensão nos permitindo diferenciar os componentes de cor vermelha, verde e azul. % Encontre as dimensoes (que sao iguais para todas as subimagens). [MAXA_row, MAXA_col] = size(imAr); % crie uma matriz grande para conter as subimagens superx = zeros(MAXA_row*2, MAXA_col*2, 3, 'uint8'); Agora combine os três componentes de cor para a aproximação A e armazene-os na grande matriz superx. % coloque A no canto superior esquerdo superx(1:MAXA_row, 1:MAXA_col,1) = imAr; superx(1:MAXA_row, 1:MAXA_col,2) = imAg; superx(1:MAXA_row, 1:MAXA_col,3) = imAb; Da mesma forma, combinamos os componentes de cor para cada um dos detalhes e os colocamos em seus respectivos cantos. % coloque B no canto superior direito superx(1:MAXA_row, MAXA_col+1:MAXA_col*2,1) = imBr; superx(1:MAXA_row, MAXA_col+1:MAXA_col*2,2) = imBg; superx(1:MAXA_row, MAXA_col+1:MAXA_col*2,3) = imBb; % coloque C no canto inferior esquerdo superx(MAXA_row+1:MAXA_row*2, 1:MAXA_col,1) = imCr; superx(MAXA_row+1:MAXA_row*2, 1:MAXA_col,2) = imCg; superx(MAXA_row+1:MAXA_row*2, 1:MAXA_col,3) = imCb; % coloque D no canto inferior direito superx(MAXA_row+1:MAXA_row*2, MAXA_col+1:MAXA_col*2,1) = imDr; superx(MAXA_row+1:MAXA_row*2, MAXA_col+1:MAXA_col*2,2) = imDg; superx(MAXA_row+1:MAXA_row*2, MAXA_col+1:MAXA_col*2,3) = imDb; Agora exibimos a imagem resultante. imshow(superx); Você pode encontrar a imagem de exemplo, colorimage.jpg, no site da LTC Editora. Ao executarmos esse programa, constatamos que a aproximação retém cores, mas os três detalhes são tons de cinza.

10.5

Execução e Reversão da Transformada Discreta de Wavelet

Para a análise de sinais que utiliza a transformada de wavelet, faz sentido examinar os resultados da DWT após a transformada, mas antes que o sinal seja completamente reconstruído. Ou seja, podemos visualizar o sinal como um conjunto de subsinais que, ao serem somados, combinam-se para reconstituir os dados originais. Esses subsinais correspondem ao que a transformada de wavelet removeu do sinal original nos diferentes níveis de resolução.

Weeks 010.indd 326

20/06/12 10:02

327

Aplicações

Existem dois programas (inclusos disponíveis no site da LTC Editora) específicos para esse tipo de análise: DWT_undo.m e DWT_undo2.m, para os casos unibidimensional e bidimensional, respectivamente. Ambos permitem que o sinal de entrada seja analisado para até oito oitavas. Tenha em mente que o tamanho da entrada limita o número de oitavas. Se J representa o número de oitavas, o sinal de entrada deve ter no mínimo 2J amostras de extensão (ou, no caso de um sinal bidimensional, deve ser no mínimo igual a 2J × 2J amostras). Para ambos os programas, os subsinais de detalhe e aproximação são do mesmo tamanho que o original. Compare-os com as funções DWT normais dwt e dwt2, onde o tamanho dos subsinais depende diretamente do número da oitava. Nossos dois programas fornecem os subsinais como de mesmo tamanho, de modo que a soma de todos os subsinais recria o original. Isso não seria apropriado para uma aplicação de compressão, mas dados extras são interessantes para a análise. O código a seguir mostra como esses programas funcionam. Primeiramente, examinaremos o caso unidimensional. x = floor(rand(1,100)*100); [A, D] = DWT_undo(x, 'db4', 3); input_recon = A + D(1,:) + D(2,:) + D(3,:); error = sum(abs(x - input_recon)) O código gera um sinal aleatório para x e calcula em seguida a DWT e a transformada discreta de wavelet inversa (IDWT) em x utilizando a wavelet db4, para três oitavas. Os resultados são retornados em uma aproximação A unidimensional de terceira oitava, e três sinais de detalhe D (D(1,:), D(2,:) e D(3,:)). A variável input_recon será uma reconstrução unidimensional do sinal original x. Ele deve ser uma cópia exata de x, o que verificamos na instrução final. Com exceção de algum erro de precisão, a diferença entre os dois sinais é zero. A saída efetiva é esta: error = 4.3289e-09 O sinal aleatório anterior funciona bem para o teste do código, mas nós o executaremos novamente com um sinal real para x. Tal como mostra o sinal abaixo, fazemos de x uma soma de senoides e depois aplicamos a função DWT_undo. Ao fazermos isso, verificamos que os resultados obtidos são os exibidos nas Figuras 10.2 e 10.3. t = 0:0.001:0.1; x = cos(2*pi*100*t) + 1.5*cos(2*pi*110*t + pi/6); [A, D] = DWT_undo(x, 'db4', 3);

Sinal original 2 1 0 –1 –2 10

20

30

40

50

60

70

80

90

100

80

90

100

Aproximação na oitava 3, desfeita 1,5 1 0,5 Figura 10.2 Resultados de DWT_undo para um sinal unidimensional: original e aproximação.

Weeks 010.indd 327

0 –0,5 10

20

30

40

50

60

70

20/06/12 10:02

328

CAPÍTULO 10

Detalhe na oitava 1, desfeito 0,2 0,1 0 –0,1 –0,2 10

20

30 40 50 60 70 Detalhe na oitava 2, desfeito

80

90

100

10

20

30 40 50 60 70 Detalhe na oitava 3, desfeito

80

90

100

1 0 –1

2 0 –2 10

20

30

40

50

60

70

80

90

100

Figura 10.3 Resultados de DWT_undo para um sinal unidimensional: detalhes para as oitavas 1-3.

A Figura 10.2 traça o sinal original na parte superior e aproximação de terceiro nível desfeita na parte inferior. Vemos que os sinais são do mesmo tamanho, o que resulta da função DWT_undo. O sinal de aproximação não é a aproximação compacta de um oitavo do tamanho, mas a contribuição do canal de aproximação, pronta para ser acrescentada às contribuições de detalhe amostra por amostra para a reconstrução do sinal. Tenha em mente que não estamos buscando a compactação aqui, mas desejamos ver como o sinal original é decomposto em subsinais. Ao examinarmos três sinais de detalhe desfeitos na Figura 10.3, podemos notar que a escala dos detalhes cresce de acordo com a oitava. O sinal de detalhe da primeira oitava contém muitas variações pequenas, enquanto o sinal de detalhe da segunda oitava possui variações maiores, embora em menor número. Os detalhes da segunda oitava teriam metade do número da primeira oitava, resultando em menos variações. As magnitudes maiores mostram que a aproximação na oitava 1 torna-se o detalhe na oitava 2. Em outras palavras, nossa ideia de detalhe varia (fica maior) em cada oitava subsequente. Assim como os detalhes tornam-se maiores em magnitude da oitava 1 para a oitava 2, o mesmo acontece entre as oitavas 2 e 3. Os detalhes na oitava 3 contêm uma característica interessante: constatamos que as magnitudes tornam-se menores por volta da amostra 40 e maiores em seguida, tal como no sinal original. Na verdade, um exame da aproximação da terceira oitava (veja o traçado inferior da Figura 10.2) revela que, antes da amostra 85, a aproximação poderia muito facilmente ser igual a 0. Os três sinais de detalhe na prática contêm informações do sinal saliente, ao menos antes das últimas amostras. Novamente, reconstruímos o sinal de entrada e computamos o erro somente para descobrir que ele é muito pequeno. >> input_recon = A + D(1,:) + D(2,:) + D(3,:); >> error = sum(abs(x - input_recon)) error = 3.3799e-10 Para o caso bidimensional, vamos começar com uma imagem. Qualquer imagem relativamente grande servirá. Nós a armazenaremos (utilizando precisão dupla) na variável x. Em seguida, chamamos DWT_undo2 para executar a DWT bidimensional e IDWT utilizando a wavelet db4, para oito oitavas. Em seguida, reconstruímos o sinal e encontramos o erro. tic x = double(imread('dog256x256.gif')); [A, B, C, D] = DWT_undo2(x, 'db4', 8); y = A;

Weeks 010.indd 328

20/06/12 10:02

Aplicações

329

for k=1:8 tempB(:,:) = B(:,:,k); tempC(:,:) = C(:,:,k); tempD(:,:) = D(:,:,k); y = y + tempB + tempC + tempD; end error= sum(sum(abs(x-round(y)))) toc A saída aparece como esta a seguir. Os comandos tic e toc nos permitem especificar o início e o fim de um cronômetro virtual para saber quanto tempo o código requer. Note que isso inclui não apenas o tempo para DWT_undo2, mas também o tempo necessário para a reconstrução. error = 0 Elapsed time is 4.191529 seconds. O computador informa erro exatamente zero? Isso resulta da função round, que basicamente converte o sinal reconstruído em valores inteiros. Como uma imagem em tons de cinza, os valores do sinal original são todos inteiros entre 0 e 255, portanto faz sentido arredondar o resultado. As matrizes que precedem B, C e D armazenam os detalhes da imagem para o número de oitavas fornecido. Ou seja, B não contém apenas a matriz de detalhe correspondente à oitava 1 – ela também armazena a matriz de detalhe para as oitavas 2, 3 etc. As diferenças entre B, C e D são os filtros que utilizamos para gerar cada uma delas (baixa-alta, alta-baixa, ou alta-alta). Essas matrizes contêm os detalhes e podem ser utilizadas para vermos bordas dentro da imagem. Na verdade, B, C e D geralmente são aludidas como os componentes horizontal, vertical e diagonal de uma imagem. É por essa razão que a documentação do MATLAB (help dwt2) lista as saídas como CA, CH, CV e CD para aproximada, horizontal, e assim por diante. A próxima seção utiliza esse programa bidimensional DWT_undo2 para realçar as bordas da imagem.

10.6

Detecção de Borda

Os defensores da transformada de wavelet geralmente alegam que ela decompõe uma imagem em detalhes horizontais, verticais e diagonais. Ao utilizarmos a imagem dog-on-porch (inclusa no CD-ROM), demonstramos o que isso significa. Além disso, exploraremos rapidamente como esses detalhes prestam-se à detecção de borda. A Figura 10.4 exibe os resultados da primeira oitava para o programa DWT_undo2, para os valores maiores. Esse programa (também fornecido no CD-ROM) calcula a DWT e desfaz em seguida a transformada em cada subsinal sem somar os resultados. Para calcularmos a reconstrução, simplesmente somamos todos os subsinais reconstituídos. Isso significa que cada subsinal resultante contém a contribuição daquele canal. Mostramos somente os maiores detalhes (os 10% maiores) e os marcamos como pixels pretos. O plano de fundo e os detalhes de menor magnitude são mapeados para pixels brancos. Desta forma, os detalhes importantes sobressaem. Podemos usar essa informação para encontrar as bordas de uma imagem [44, 45]. A detecção de borda constitui uma etapa importante na segmentação da imagem, utilizada para separar uma área de interesse do plano de fundo. Por exemplo, os dados da borda podem ser utilizados para encontrar e medir automaticamente as características de uma imagem de célula cerebral [46]. A começarmos no quadrante superior esquerdo da Figura 10.4, encontramos o local onde a aproximação ocorreria. Nós o deixamos em branco para as oitavas 1, 2 e 3, pois essa aproximação produz a transformada na próxima oitava. A seção superior direita mostra onde os detalhes principais estão localizados. A partir dela podemos observar o contorno básico do cão. Vemos esse contorno em menor grau no canto inferior direito e um pouco melhor novamente no canto inferior esquerdo. Esta figura como um todo exibe algumas bordas e uma grande quantidade de “ruído”. (Na prática, trata-se de dados do plano

Weeks 010.indd 329

20/06/12 10:02

330

CAPÍTULO 10

Oitava 1 50 100 150 200 250 300 350 400 450

500

Figura 10.4 Resultados de DWT_undo para um sinal bidimensional: detalhes para a oitava 1.

500

Figura 10.5 Resultados de DWT_undo2 para um sinal bidimensional: detalhes para a oitava 2.

500 50

100

150

200

250

300

350

400

450

Oitava 2 50 100 150 200 250 300 350 400 450 500 50

100

150

200

250

300

350

400

450

de fundo, e não de ruído.) De modo geral, a contribuição procedente da primeira oitava contém dados que mudam muito rápido. A seguir na Figura 10.5, vemos segmentos de linha emergindo do traçado. A partir do canto superior direito no sentido horário, os segmentos de linha horizontais são claramente visíveis, seguidos de linhas diagonais. Em seguida, temos linhas verticais no canto inferior esquerdo. Esta imagem respalda a afirmação de que a transformada de wavelet seleciona detalhes horizontais, verticais e diagonais. Vemos esse padrão novamente na Figura 10.6, para os detalhes da terceira oitava. Além disso, observamos que a natureza da informação muda à medida que exploramos oitavas adicionais. Onde os dados da primeira oitava contêm muita informação de aparência ruidosa, essa terceira oitava possui primordialmente bordas de interesse. Podemos ver um contorno do cão, bem como algumas linhas diagonais dos degraus da escada. Os segmentos de linha horizontais (canto superior direito) e verticais (canto inferior esquerdo) são mais grossos do que aqueles nas oitavas anteriores, devido ao escalonamento da wavelet.

Weeks 010.indd 330

20/06/12 10:02

331

Aplicações

Oitava 3 50 100 150 200 250 300 350 400 450 Figura 10.6 Resultados de DWT_undo2 para um sinal bidimensional: detalhes para a oitava 3.

500 50

100

150

200

250

300

350

400

450

500

A Figura 10.7 mostra a contribuição da quarta oitava para a imagem dog-on-porch. Note como as três subimagens mostram bem os detalhes horizontais, diagonais e verticais (no sentido horário a partir do canto superior direito). No canto superior esquerdo, vemos onde os 10% maiores dentre os dados de aproximação se encontram. Os segmentos de linha que vemos nesta figura são ainda maiores que nas oitavas anteriores. Ir além disso para a oitava 5 não faz muito sentido. Para termos uma ideia da quantidade de dados disponíveis na aproximação, começamos com 256 × 256 pixels na imagem original, depois tivemos 128 × 128 valores na aproximação da primeira oitava, 64 × 64 elementos na oitava 2, um tamanho de dados de 32 × 32 para a oitava 3 e, por fim, 16 × 16 para a aproximação final. Como uma observação adicional, tenha em mente que as imagens nestas figuras foram ampliadas de volta ao tamanho original. Embora sejam mais imprecisos nas oitavas subsequentes, os números que aparecem nos lados das imagens não mudam, por corresponderem às coordenadas do pixel expandido. Ou seja, todas as imagens são compostas de quatro imagens de 256 × 256 e exibem 512 × 512 pixels no total.

Oitava 4 50 100 150 200 250 300 350 400 450 Figura 10.7 Resultados de DWT_undo2 para um sinal bidimensional: detalhes para a oitava 4.

Weeks 010.indd 331

500 50

100

150

200

250

300

350

400

450

500

20/06/12 10:02

332

CAPÍTULO 10

Valores maiores combinados a partir das oitavas 2, 3 e 4

50

100

150

200

250 50

100

150

200

250

Figura 10.8 Combinação dos detalhes das oitavas 2, 3 e 4.

Como utilizar esses dados para definir bordas constitui o objeto de pesquisa em andamento [47]. Poderíamos somar os dados de detalhe e observar os maiores valores nas bordas, mas a utilização de todos os dados gera muitos falsos positivos, bordas menores que preferiríamos ignorar. A utilização de um subconjunto limitado de dados pode gerar rupturas nas bordas. Precisamos encontrar um algoritmo ótimo que realce as bordas importantes. A Figura 10.8 mostra os detalhes combinados a partir das oitavas 2, 3 e 4. Embora ela preserve a maioria das bordas, notamos a existência de lacunas. Uma solução possível consiste em intensificar as bordas com base em sua oitava. Ou seja, podemos utilizar dados das oitavas 2 e 3 para definir nossas bordas e utilizar a oitava 1 somente para preencher as lacunas. Uma ideia promissora seria limitar os dados por oitava. Em vez de examinarmos 10% dos maiores valores de todas as oitavas, poderíamos limitar a contribuição das oitavas inferiores, tal como considerar apenas 1% dos maiores valores da oitava 1. Os programas utilizados para gerar essas imagens (test2_DWT_undo2.m e top10image.m) podem ser encontrados no CD-ROM.

10.7

Solução Recursiva de um Quebra-Cabeça Sudoku

Sudoku é um quebra-cabeça no qual os dígitos de 1 a 9 aparecem exatamente uma vez em cada linha, coluna ou submatriz 3 × 3. O quebra-cabeça é fornecido com algumas das casas numéricas já preenchidas, e o objetivo é preencher as restantes. Imagine, por exemplo, uma linha com os primeiros oito números fornecidos como {3, 1, 5, 9, 7, 6, 8, 4} seguidos de uma casa em branco. Sabemos que o número correspondente à casa em branco não pode ser uma repetição de qualquer um dos números já presentes na linha, e pelo processo de eliminação chegamos à conclusão de que o número correto é 2. Graças a uma lógica similar, somos capazes de completar o quebra-cabeça. Um Sudoku bem construído possui apenas uma solução. Mas como saber se esse é o caso? Podemos tratar o quebra-cabeça como uma matriz e utilizar o MATLAB para resolvê-lo para nós. Claro que isso tiraria toda a graça, mas iremos um passo além: faremos com que o MATLAB encontre todas as soluções possíveis. Para resolver este problema, faremos uso da recursão. Começaremos com o quebra-cabeça em uma forma incompleta, com zeros para as casas em branco. Em seguida, encontraremos a primeira casa em branco (zero) e colocaremos um palpite em seu lugar. A seguir, verificaremos se nosso palpite causa um problema: se esse número já existe na linha, coluna ou submatriz 3 × 3. Se houver um conflito, repetiremos o processo com um novo palpite. Para simplificar as coisas, nossos palpites sempre começarão com 1, então serão incrementados para 2, depois incrementados para 3, e assim por diante até chegarmos a 9.

Weeks 010.indd 332

20/06/12 10:02

Aplicações

333

A Tabela 10.1 mostra um exemplo de Sudoku com zeros no lugar das casas em branco. Caso você não tenha visto este quebra-cabeça antes, tente resolvê-lo por conta própria. A Tabela 10.2 fornece a resposta. Um quebra-cabeça Sudoku de exemplo.

Tabela 10.1

0 4 7 3 0 6 2 9 0

Tabela 10.2

2 0 8 1 0 9 7 0 6

3 6 0 2 0 8 0 1 4

4 7 1 0 0 0 3 6 9

0 0 0 0 0 0 0 0 0

6 9 3 0 0 0 8 2 5

7 1 0 9 0 5 0 8 2

8 0 5 7 0 1 9 0 3

0 3 6 8 0 4 1 5 0

A solução do quebra-cabeça Sudoku de exemplo. 1 4 7 3 5 6 2 9 8

2 5 8 1 4 9 7 3 6

3 6 9 2 7 8 5 1 4

4 7 1 5 8 2 3 6 9

5 8 2 6 9 3 4 7 1

6 9 3 4 1 7 8 2 5

7 1 4 9 3 5 6 8 2

8 2 5 7 6 1 9 4 3

9 3 6 8 2 4 1 5 7

Para resolver o problema, utilizaremos três arquivos MATLAB: um programa para fornecer o quebracabeça incompleto e iniciar o processo, uma função para verificar se o quebra-cabeça é válido e uma função para procurar casas em branco e preenchê-las com palpites. Chamaremos o programa de partida de sudoku.m, a função que verifica o quebra-cabeça de sudoku_check.m e a função de busca e palpite de sudoku_search.m. Primeiramente, vamos examinar o programa de partida. Definimos o quebra-cabeça como uma matriz, com zeros no lugar das casas em branco. Em seguida, conferimos o quebra-cabeça para nos certificar de que ele é válido, utilizando a função sudoku_check. Caso não seja, a função de busca nunca encontrará uma solução; portanto devemos fazer isso antecipadamente. A seguir, chamamos nossa função sudoku_ search. puzzle = [ 0, 4, 7, 3, 0, 6, 2, 9, 0,

2, 0, 8, 1, 0, 9, 7, 0, 6,

3, 6, 0, 2, 0, 8, 0, 1, 4,

4, 7, 1, 0, 0, 0, 3, 6, 9,

0, 0, 0, 0, 0, 0, 0, 0, 0,

6, 9, 3, 0, 0, 0, 8, 2, 5,

7, 1, 0, 9, 0, 5, 0, 8, 2,

8, 0, 5, 7, 0, 1, 9, 0, 3,

0; 3; 6; 8; 0; 4; 1; 5; 0]

if (sudoku_check(puzzle)) sudoku_search(puzzle); end A função que verifica o quebra-cabeça vem a seguir. Seu funcionamento consiste simplesmente em isolar cada linha e contar a ocorrência de cada um dos números de 1 a 9. Se ela encontrar mais de uma

Weeks 010.indd 333

20/06/12 10:02

334

CAPÍTULO 10

ocorrência de um número, definirá o flag3 OK como false para indicar que o quebra-cabeça possui uma entrada inválida. Em seguida, verificamos cada coluna de forma semelhante. Por fim, verificamos cada submatriz. Uma vez que a função esteja concluída, ela retorna o flag OK. O programa de chamada entrega a sudoku_check.m um quebra-cabeça e ele devolve um simples valor true ou false. Em vez de mostrarmos a função completa, mostraremos apenas a primeira parte que verifica cada linha. Procure na listagem de funções no CD-ROM as verificações das colunas e submatrizes. % sudoku_check.m % % Verifique um quebra-cabeca Sudoku – % certifique-se de que cada numero aparece em apenas uma linha, % coluna e submatriz 3x3. % function OK = sudoku_check(puzzle) % considere o quebra-cabeca correto, ate conclusao em contrario OK = true; % Verifique se a matriz do quebra-cabeca esta correta % Primeiramente, verifique as linhas. r = numero da linha for r = 1:9 % Isole uma linha do quebra-cabeca row = puzzle(r, 1:9); % considere que n seja o numero (1..9) for n = 1:9 % inicialize a contagem para este numero num_count = 0; % examine cada coluna for c = 1:9 % se a entrada do quebra-cabeca corresponder ao numero % que estamos procurando, acrescente um a contagem. if (row(c) == n) num_count = num_count + 1; end end % certifique-se de que cada numero 1..9 apareca 1) % encontramos uma duplicata nesta linha OK = false; end end end % Veja sudoku_check.m para a verificacao da coluna % e a verificacao da submatriz 3x3 Nosso objetivo é mostrar todas as soluções possíveis. Como não precisamos guardar as soluções que encontramos, simplesmente as exibiremos na tela. A função sudoku_search encontra as casas em branco e, caso não encontre nenhuma, imprime o quebra-cabeça. Esta função de busca recebe uma matriz de quebra-cabeça que pode ou não estar completa. Ela define uma variável chamada blank que utilizamos para indicar a presença de uma ou mais casas em branco no quebra-cabeça. Na prática, não nos importamos com o número de casas em branco, apenas queremos saber se há algum. Assim, inicialmente consideramos que não há casas em branco e depois examinamos a matriz. Se encontrarmos uma, atualizamos nossa variável blank para indicar isso e prosseguimos para a parte seguinte. Se encontrarmos uma casa em branco na primeira posição que examinarmos, por que deve-

3

Weeks 010.indd 334

Indicador. (N.T.)

20/06/12 10:02

Aplicações

335

remos nos preocupar em examinar o resto do quebra-cabeça? Isso explica por que utilizamos loops while em vez de loops for – é possível que desejemos sair do loop prematuramente. Na parte seguinte, existem duas possibilidades. Ou encontramos uma casa em branco e saímos dos loops prematuramente, ou examinamos o quebra-cabeça inteiro e não encontramos uma. Consideramos que todos os quebra-cabeças que passaram pela função de busca são válidos, de modo que, se não houver casas em branco, então deveremos necessariamente ter uma solução. Neste caso, exibimos a matriz e saímos da função. Se encontrarmos uma casa em branco, precisaremos tentar um palpite para aquela posição. Uma vez que o palpite esteja definido, precisaremos nos certificar de que ele é válido. Mas o fato de ele ser válido não significa que seja parte da solução – poderemos descobrir mais tarde que ele acarreta uma contradição. Por exemplo, ao examinarmos o quebra-cabeça na Tabela 10.1, vemos que o canto superior esquerdo possui uma casa em branco e que tanto 1 quanto 5 são possibilidades válidas neste caso. Contudo, somente uma dessas possibilidades é a resposta correta. Quando possuímos um palpite válido em nosso quebra-cabeça, precisamos de uma forma de procurar mais casas em branco e preenchê-las. (Isso não soa familiar?) É aqui que entra a recursão: a função de busca chama a si mesma com o quebra-cabeça atualizado! O computador monitora as funções que chamamos e permite que chamemos uma função de dentro da própria função. Naturalmente, precisamos ser cuidadosos aqui – executada sem os devidos cuidados, a função poderia chamar a si mesma repetidas vezes indefinidamente, correndo o risco de travar o computador como resultado. Nossa solução funcionará porque damos um passo na direção certa toda vez que chamamos uma função de busca. Existem muitas chamadas que poderíamos fazer, mas elas são finitas. Podemos até mesmo começar com uma matriz de zeros, e o computador exibirá cada solução possível para o Sudoku. Nossa solução também repousa no fato de que a função de busca possui uma palavra chave function em sua definição. O computador manterá uma cópia de seus dados separada de outras funções. Ele aloca variáveis conforme o necessário e as descarta quando a função é finalizada. Em vez de haver uma matriz chamada puzzle, na prática há muitas delas, dependendo do número de chamadas que fizemos para sudoku_ search. Todas essas versões diferentes do quebra-cabeça trabalham a nosso favor, por permitirem uma busca sistemática de todas as possíveis soluções pelo computador.4 Ou seja, se a função de busca inserir um palpite ruim (embora válido), ela chamará a si mesma com esse palpite definido. Se a próxima chamada da função de busca não conseguir fazer com que o quebra-cabeça funcione, o controle retornará à chamada anterior da função e esta tentará um novo palpite. O código para a função de busca vem a seguir: % % sudoku_search % % Tente preencher recursivamente um quebra-cabeca Sudoku. % function blank = sudoku_search(puzzle) % considere que nao existem casas em branco, % ate que encontremos uma blank = false; % Parte I % examine o quebra-cabeca ate encontrarmos uma casa em branco % ou obtenha a ultima posicao do quebra-cabeca row = 1; % examine as linhas while ((row puzzle = [ 0, 0, 7, 3, 0, 6, 2, 9, 0,

2, 0, 8, 1, 0, 9, 7, 0, 6,

3, 6, 0, 2, 0, 8, 0, 1, 4,

4, 7, 1, 0, 0, 0, 3, 6, 9,

0, 0, 0, 0, 0, 0, 0, 0, 0,

6, 9, 3, 0, 0, 0, 8, 2, 5,

7, 1, 0, 9, 0, 5, 0, 8, 2,

8, 0, 5, 7, 0, 1, 9, 0, 3,

0; 3; 6; 8; 0; 4; 1; 5; 0]

>> sudoku_search(puzzle);

Weeks 010.indd 336

20/06/12 10:02

Aplicações

337

Pouco depois, o MATLAB responde com a seguinte saída: puzzle = 1 4 7 3 5 6 2 9 8

2 5 8 1 4 9 7 3 6

3 6 9 2 7 8 5 1 4

4 7 1 5 8 2 3 6 9

5 8 2 6 9 3 4 7 1

6 9 3 4 1 7 8 2 5

7 1 4 9 3 5 6 8 2

8 2 5 7 6 1 9 4 3

9 3 6 8 2 4 1 5 7

2 4 8 1 5 9 7 3 6

3 6 9 2 7 8 5 1 4

4 7 1 5 8 2 3 6 9

5 8 2 6 9 3 4 7 1

6 9 3 4 1 7 8 2 5

7 1 4 9 3 5 6 8 2

8 2 5 7 6 1 9 4 3

9 3 6 8 2 4 1 5 7

puzzle = 1 5 7 3 4 6 2 9 8

Como podemos ver na saída, o quebra-cabeça agora possui duas soluções. Um exame das duas soluções mostra que as colunas 1 e 2 nas linhas 2 e 5 constituem as únicas diferenças.

10.8

Resposta de Magnitude em Frequência de Som

O programa a seguir traça a resposta de magnitude em frequência de som. Uma vez que ela se repete em um loop, o gráfico parece mudar em tempo (quase) real. As primeiras linhas definem as variáveis para poupar tempo mais tarde. Isso inclui uma lista que utilizaremos para mostrar somente a primeira metade da informação de frequência, bem como outra lista para o eixo x, para mostrá-lo em termos de frequências reais em vez de números de amostra. Examine show_sound.m no CD-ROM. figure(1); % traga a figura 1 para a frente fs = 44100; N = 4410*1; half = 1:ceil(N/2); MaxFreq = ceil(max(half)*fs/N); xaxis = half*fs/N; A variável N é configurada para um décimo da taxa de amostragem. A multiplicação por um não a afeta, mas você pode querer experimentar mudando de um para dois. Se seu computador possui a função waverecord (“gravar onda”), esse parâmetro seleciona a quantidade de dados que o computador lê em uma chamada de waverecord. Em seguida, tentamos utilizar o recurso audiorecorder (“gravador de áudio”) do MATLAB. Ou ele será suportado, ou o programa tentará mais tarde utilizar wavrecord em seu lugar. O código não é perfeito, contudo, pois alguns computadores equipados com uma versão mais antiga do MATLAB podem também não ser capazes de utilizar a função wavrecord.

Weeks 010.indd 337

20/06/12 10:02

338

CAPÍTULO 10

% detecte se try audiorec x_object catch audiorec disp('No end

podemos utilizar o audiorecorder = 1; % Yes = audiorecorder(44100, 16, 1); = 0; % No audiorecorder support.');

A parte central do programa é um loop for que grava um som e o exibe na tela. O comando pause deve estar funcionando para dar ao sistema uma chance de atualizar a tela. Os demais comandos simplesmente melhoram a aparência e a consistência do traçado. for i=1:10 % grave algum som if (audiorec) recordblocking(x_object, 0.5); x = getaudiodata(x_object, 'double'); else x = wavrecord(N, fs, 1); end % encontre as magnitudes de FFT X = abs(fft(x)); % trace a FMR plot(xaxis, X(half)); % melhore a aparencia do tracado axis([0 MaxFreq 0 100]); xlabel('Frequencia (Hz)'); ylabel('Amplitude'); title('Resposta de Magnitude em Frequencia para som'); % De tempo ao sistema para exibir o grafico pause(0.01); end

Resposta de Magnitude em Frequência para som

100 90 80

Amplitude

70 60 50 40 30 20 10 0

Weeks 010.indd 338

0

0,5

1 Frequência (Hz)

1,5

2

Figura 10.9 Exemplo do programa show_sound.

20/06/12 10:02

Aplicações

339

Ao ser executado, o programa grava som e mostra sua resposta de magnitude em frequência (FMR) o mais rapidamente possível. Para que ele demore mais, apenas ajuste os números na linha for. Além disso, os usuários com suporte à wavrecord e com uma versão mais recente do MATLAB podem querer experimentar ambas as funções de gravação. A função wavrecord permite um intervalo de tempo de gravação menor para parecer que o programa está rodando em tempo real. A Figura 10.9 contém um exemplo da saída do programa, onde a entrada do microfone é o som de um apito. À medida que o programa é executado, o gráfico muda de acordo com o sinal de entrada. Com esse programa, podemos ver como os sons simples (como o apito) são no domínio da frequência ou visualizar sinais mais complexos, como uma conversa. Se um instrumento musical estiver sendo tocado nas proximidades, os harmônicos das notas aparecerão como picos no domínio da frequência.

10.9

Projeto de Filtro

Vimos como vários filtros afetam um sinal. Mas como determinar os coeficientes de filtro a serem utilizados para se obter um determinado efeito? Nesta seção, examinaremos o projeto de filtros FIR.

10.9.1

Métodos de Janelamento

O MATLAB disponibiliza a função fir1 para o projeto de filtros FIR na caixa de ferramentas de Processamento de Sinal (Signal Processing). Tal como sua documentação assinala, essa função utiliza o método de janelamento. Isso significa que os coeficientes de filtro possuem uma elevação gradual baseada na janela fornecida, para minimizar os efeitos funestos causados por transições repentinas. Considere, por exemplo, o código a seguir. Temos uma função impulso x, filtrada por dois filtros diferentes. O primeiro passo consiste em criar a função impulso, denominada imp. imp = zeros(1, 1000); imp(50) = 1; Agora filtraremos isso com os coeficientes de filtro {1, 1, 1, 1, 1}. Uma vez filtrado, mostramos a resposta de magnitude em frequência. Examine filter_test.m no CD-ROM. myfilt = [1, 1, 1, 1, 1]; y = conv(imp, myfilt); % filtre o impulso Y = fft(y); % encontre a resposta de frequencia maxY = max(abs(Y)); half = 1:ceil(length(y)/2); subplot(2,1,1); plot(half, abs(Y(half))/maxY, 'b'); title('FMR para coeficientes de filtro [1, 1, 1, 1, 1]'); axis tight; % estenda o grafico para tracar limites Podemos comparar isso ao efeito de outro filtro, um onde os coeficientes de filtro aproxima-se mais gradualmente de 1 e decai gradualmente em seguida. Chamamos esse filtro de myfilt2. myfilt2 = [1/3, 2/3, 1, 2/3, 1/3]; y2 = conv(imp, myfilt2); % filtre o impulso Y2 = fft(y2); % encontre a resposta de frequencia maxY2 = max(abs(Y2)); subplot(2,1,2); plot(half, abs(Y2(half))/maxY2, 'b'); title('FMR para coeficientes de filtro [1/3, 2/3, 1, 2/3, 1/3]'); axis tight; % estenda o grafico para tracar limites

Weeks 010.indd 339

20/06/12 10:02

340

CAPÍTULO 10

FMR para coeficientes de filtro [1, 1, 1, 1, 1] 1 0,8 0,8 0,4 0,2 50

100

150

200

250

300

350

400

450

500

450

500

FMR para coeficientes de filtro [1/3, 2/3, 1, 2/3, 1/3]

1 0,8 0,8 0,4 0,2 50

100

150

200

250

300

350

400

Figura 10.10 Dois filtros similares, com e sem uma transição gradual.

A partir do gráfico, mostrado na Figura 10.10, vemos que ambos os conjuntos de coeficientes de filtro produzem um filtro passa-baixa. Entretanto, o gráfico superior mostra um lobo lateral de tamanho considerável em que a resposta de frequência torna-se maior e depois menor. Embora a resposta de frequência seja muito pequena em torno do índice 200, vemos que ela se torna muito maior após esse ponto. No outro gráfico, vemos a resposta de frequência aproximar-se de zero um pouco mais devagar (dizemos que ela possui um lobo principal mais largo). Vemos também que ela possui um lobo lateral muito menor. Estes gráficos demonstram o efeito de uma janela. Na verdade, os coeficientes do segundo filtro foram escolhidos a partir da janela triangular simples. Em outras palavras, o primeiro conjunto de coeficientes de filtro foi janelado para criar o segundo conjunto. O comando a seguir retorna a função janela triangular de 5 valores. Note que a função window faz parte da caixa de ferramentas de Processamento de Sinal no MATLAB. >> w = window(@triang, 5) w = 0.3333 0.6667 1.0000 0.6667 0.3333 Agora que temos uma ideia de como as janelas se aplicam aos coeficientes de filtro, retornamos ao tópico do projeto de filtros FIR. Se tivermos uma função impulso no domínio do tempo, precisaremos de um número infinito de valores no domínio da frequência para representá-la. A recíproca é verdadeira – um impulso no domínio da frequência requer um número infinito de pontos no domínio do tempo. Para criarmos um filtro no domínio do tempo, decidimos como sua resposta no domínio da frequência deve ser. Podemos controlar a frequência de corte, aquela frequência acima da qual um filtro passa-baixo começa a zerar o conteúdo de frequência. Em condições ideais, isso seria uma função binária na qual o conteúdo de frequência abaixo do limite de corte permaneceria o mesmo, enquanto o conteúdo acima do limite de corte seria zero. Na prática, o melhor que podemos esperar é uma inclinação acentuada. Também podemos controlar o número de coeficientes de filtro de que dispomos, chamados de taps ou ordem. (A ordem equivale ao número de taps menos 1.) Assim, para um meio termo razoável, janelamos os coeficientes de filtro para o número de taps fornecido. Um número maior de coeficientes proporciona uma resposta de frequência que mais se aproxima do ideal, mas às custas de mais processamento (seja mais processamento de software ou maior área de hardware necessária para a implementação dos filtros).

Weeks 010.indd 340

20/06/12 10:02

Aplicações

10.9.2

341

Projeto de um Filtro FIR

Vamos tentar projetar um filtro FIR. Começaremos com a resposta de frequência que desejamos e trabalharemos a partir dela. Para simplificar, teremos um filtro passa-baixo com uma frequência de corte em 30%. Precisamos de uma função impulso com a qual trabalhar; portanto definimos e utilizamos imp. Utilizaremos a extensão MAXLEN algumas vezes mais, de modo que também podemos defini-la agora. % crie uma funcao impulso MAXLEN = 1000; imp = zeros(1, MAXLEN); imp(50) = 1; Em seguida, precisamos definir nossa frequência de corte em termos do número de valores no domínio da frequência a serem utilizados: 150 de 500 correspondem a 30%; por isso usaremos esse valor. (O 500 advém da metade do tamanho de espectro de MAXLEN.) Além disso, definiremos o número de coeficientes a serem utilizados. low_pass_end = 150; taps = 51; Simulamos a resposta que desejamos no domínio da frequência como uma função ideal, fx. A maior parte da resposta deve ser zero, exceto para o limite inferior. Desse modo criamos uma lista binária, na qual inserimos uns no início e zeros após o limite de corte desejado. O filtro deve permitir que os 30% de frequências inferiores passem por ele. Além disso, definimos a metade superior da função ideal fx como uma imagem espelhada da metade inferior, como esperaríamos de uma FMR de uma função de valores reais. (Isso corresponde à metade superior da FMR que não traçamos.) fx = zeros(1, MAXLEN); fx(1:low_pass_end) = 1; % passa-baixo % inclui imagem espelhada de passa-baixo fx((MAXLEN-low_pass_end):MAXLEN) = 1; Uma vez que nossa resposta de frequência ideal esteja especificada, nós a convertemos para o domínio do tempo. Nós a deslocamos para mover o centro em torno do ponto central, e não em torno de zero. Podemos fazer isso porque as magnitudes dos dados no domínio da frequência permaneceriam as mesmas, apenas as fases mudariam (consulte o material sobre a teoria de deslocamento da DFT, na Seção 6.5). % converta para o dominio do tempo x = ifft(fx); % desloque os dados de frequencia mid_point = floor(MAXLEN/2); x2 = [x(mid_point+1:MAXLEN), x(1:mid_point)]; Obtemos o número solicitado de coeficientes de filtro tomando um número igual antes e depois do ponto central. Incluindo o ponto central, temos um número ímpar de coeficientes de filtro (o que explica por que selecionamos 51 para o número de taps anteriormente). half_taps = floor((taps-1)/2); b2 = x2(mid_point - half_taps : mid_point + half_taps); A variável b2 armazena os coeficientes de filtro que desejamos utilizar. Entretanto, não podemos utilizá-los diretamente, pois constituem valores complexos. Podemos ser tentados a utilizar a função abs para convertê-los em valores reais, mas neste caso eles seriam todos positivos. Para preservar os sinais,

Weeks 010.indd 341

20/06/12 10:02

342

CAPÍTULO 10

podemos examinar cada valor e armazenar seu sinal, bem como sua magnitude. No processo, criamos uma nova variável c2 para guardar esses valores. for k=1:length(b2) if (sign(b2(k)) < 0) c2(k) = -abs(b2(k)); else c2(k) = abs(b2(k)); end end Enfim, temos coeficientes de filtro que podemos utilizar. Aqui omitimos os detalhes para o traçado desses coeficientes e a FMR resultante, mas o programa filter_test2.m contém os comandos. A Figura 10.11 exibe os resultados.

Coeficientes de filtro (sem janelamento)

0,3 0,2 0,1 0,0 5 1

10

15

20

25

30

35

40

45

50

FMR para filtro, utilizando ifft para gerar coeficientes de filtro

0,8 0,6 0,4 0,2 10

20

30

40

50

60

70

80

90

100

Figura 10.11 Utilização de coeficientes de filtro sem janelamento.

Conforme vemos na Figura 10.11, a FMR contém ripples. Vimos que o janelamento pode reduzir esses ripples, portanto em seguida melhoraremos nossos coeficientes de filtro com uma janela. Aqui empregamos uma janela Hamming, uma das muitas escolhas possíveis. Com a janela w, multiplicamos cada valor em c2 para criar um novo conjunto de coeficientes de filtro, d2. Você pode notar a transposição na janela w – isso simplesmente transforma um vetor de coluna em um vetor de linha, de modo que a operação.* não apresenta problemas de incompatibilidade entre dimensões de matriz. w = window(@hamming, taps); d2 = w.' .* c2; A Figura 10.12 exibe os novos coeficientes de filtro e uma FMR aprimorada. À primeira vista, os coeficientes de filtro exibidos nesta figura parecem ser exatamente como aqueles da Figura 10.11. As diferenças são mais bem vistas nos primeiros 10 e nos últimos 10 coeficientes de filtro. A FMR parece ser muito melhor na Figura 10.12, pois os ripples foram suavizados. (Os ripples ainda estão lá, apenas reduzidos substancialmente, tal como um traçado logarítmico mostraria.)

Weeks 010.indd 342

20/06/12 10:02

Aplicações

343

Coeficientes de filtro com janela 0,25 0,2 0,15 0,1 0,05 0,0 –0,05

5

10

15

20

25

30

35

40

45

50

FMR para filtro, utilizando ifft+janela para gerar coeficientes de filtro 1 0,8 0,6 0,4 0,2 10

20

Figura 10.12

30

40

50

60

70

80

90

100

Utilização de coeficientes de filtro com janelamento.

Agora vamos examinar o que o comando fir1 fará por nós. Aqui solicitamos o mesmo número de coeficientes de filtro, com a mesma frequência de corte (30%). Solicitamos taps – 1 coeficientes, pois a função espera a ordem. Ou seja, começando a contagem a partir de 0, a ordem é o valor de índice mais alto. Portanto, fir1 retornará um coeficiente a mais do que especificamos. b1 = fir1(taps-1, 0.3); A Figura 10.13 mostra os dois conjuntos de coeficientes de filtro e suas respectivas respostas de magnitude em frequência. Os coeficientes de filtro parecem ser muito semelhantes (e efetivamente são muito semelhantes em valor), muito embora não estejam alinhados. Para as FMR, vemos que os traçados estão sobrepostos. Para realmente notar uma diferença, precisaríamos de um traçado logarítmico. Coeficientes de filtro

0,3 0,2 0,1 0 5

10

15

20

25

30

35

40

45

50

FMR para filtro, ifft+janela versus fir1

1 0,8 0,6 0,4 0,2 10 Figura 10.13

Weeks 010.indd 343

20

30

40

50

60

70

80

90

100

Coeficientes de filtro com janelamento e aqueles gerados por fir1.

20/06/12 10:02

344

CAPÍTULO 10

Os coeficientes de filtro FIR de nosso exemplo são bem próximos daqueles gerados pela função fir1 no MATLAB. A partir desta discussão, você deve ser capaz de gerar coeficientes para quaisquer filtros passa-baixo que desejar simplesmente mudando os parâmetros (número de taps e frequência de corte). Mas e se você não deseja um filtro passa-baixo? A multiplicação dos coeficientes de filtro por uma senoide tem o efeito de deslocar a resposta de magnitude em frequência no domínio da frequência. Uma senoide de alta frequência saltaria de −1 a +1, o que nós simulamos. Uma modificação final dos coeficientes de filtro será a aplicação de uma inversão alternada (um sim outro não) ao sinal dos valores. f2 = d2; flip = 1; % inverta um sinal sim outro nao for k=1:length(f2) if (flip == 1) f2(k) = -f2(k); flip = 0; else flip = 1; end end

Coeficientes de filtro (cada sinal alternado trocado) 0,2 0,1 0 –0,1 –0,2 5

10

15

20

25

30

35

40

45

50

FMR para filtro, utilizando ifft+janela+alt.flip para coeficientes de filtro 1 0,8 0,6 0,4 0,2 10 Figura 10.14

20

30

40

50

60

70

80

90

100

Inversão alternada para os coeficientes de filtro com janelamento.

Ao rodarmos este código, nossos coeficientes de filtro irão se parecer com aqueles na Figura 10.14. À medida que a FMR aparece, o filtro passa a atenuar frequências baixas e a deixar passar frequências altas. Portanto, criamos um filtro passa-alto. Também podemos fazer com que o MATLAB nos forneça um filtro passa-alto, novamente com o comando fir1. Note como utilizamos 0.7 em vez de 0.3 para o limite de corte. Qual teria sido o resultado se o mantivéssemos em 0.3? b3 = fir1(taps-1, 0.7, 'high'); Em resumo, definimos nossa banda passante com fx, uma resposta de frequência idealizada. Em seguida encontramos a transformada inversa de Fourier correspondente para obter dados no domínio do tempo, os quais rotulamos de x. Deslocamos o centro de x e chamamos o resultado de x2. Mas este continha um excesso de dados, de modo que copiamos somente o número que desejávamos para uma lista denomi-

Weeks 010.indd 344

20/06/12 10:02

Aplicações

345

nada b2. Uma vez que b2 possui valores complexos, utilizamos a função abs para convertê-los em valores reais, c2, preservando o sinal. Em seguida, utilizamos c2 como um filtro passa-baixo, mas ele apresenta um ripple muito alto. Assim, nós o aprimoramos janelando os coeficientes, chamando o resultado de d2. Esses coeficientes de filtro geram uma boa resposta de frequência e lembram muito b1, os coeficientes que o comando fir1 do MATLAB retorna. Criamos em seguida f2 com uma inversão alternada (negativação) de valores de d 2 e vimos que ela nos fornece um filtro passa-alto. Para referência, também criamos um conjunto de coeficientes de filtro passa-alto, b3, utilizando o comando fir1 do MATLAB. Esta seção nos fornece uma visão geral das funções de janelamento e mostra por que elas são úteis. Além disso, exploramos a confecção de nossos próprios filtros FIR, ambos com um comando da caixa de ferramentas de Processamento de Sinais do MATLAB, assim como nosso programa. Embora esse programa utilize o comando window, que também faz parte da caixa de ferramentas de Processamento de Sinais, deve estar claro para o leitor como isso poderia ser feito sem essa caixa de ferramentas.

10.10

Compressão

Os programas de compressão removem a redundância dos dados de modo a armazená-los em uma forma compacta. Existem algoritmos de compressão com perdas (lossy), que toleram alguma perda de informação. Por exemplo, os padrões de compressão populares JPEG e MP3 (arquivos com extensões .jpg e .mp3, respectivamente) não retornam uma cópia exata do original para o usuário, mas uma versão parecida o suficiente para ele não notar a diferença após descomprimi-la. Outra possibilidade é um algoritmo de compressão sem perdas (lossless). Programas como gzip e winzip armazenam dados de forma compacta, mas não perdem qualquer informação no processo. Obviamente há ocasiões em que uma compressão com perdas funciona bem, tais como aquelas com imagens para uma página da web, e há outras ocasiões em que uma compressão sem perdas é mais apropriada, como durante o armazenamento de uma página ou de um documento de edição de texto. JPEG significa Joint Photographic Experts Group.5 Esse grupo estabeleceu dois padrões para imagens, a saber: o padrão JPEG básico e o JPEG 2000 (com extensão .jp2). Os algoritmos de compressão compreendem três etapas: transformação, quantização e codificação de entropia. A aplicação de uma transformada deve remover parte da redundância nos dados. A quantização mapeia os dados para um número definido de valores, tal como o arredondamento dos dados transformados para o inteiro mais próximo. Naturalmente, dados podem ser perdidos nessa etapa. A etapa final de compressão, a codificação de entropia, representa os dados quantizados em uma forma compacta. Isso parece muito com o modo pelo qual as pessoas se comunicam por meio de aplicações de mensagem instantânea – para que digitar “não achei que estava caro, e você?” quando a expressão “n axei kro, e vc?” transmite a ideia com menos da metade do número de caracteres?

10.10.1

Experiências com Compressão

Vamos fazer uma experiência com uma imagem para verificar como a compressão JPEG a afeta. Primeiramente, precisamos de uma imagem apropriada não comprimida. O código a seguir utiliza um arquivo denominado PIA02187.tif, uma imagem do planeta Marte. (Essa imagem foi cedida pelo Laboratório de Propulsão a Jato da NASA, em http://photojournal.jpl.nasa.gov/targetFamily/Mars, postada em 17 de fevereiro de 2006.) A extensão .tif significa que a imagem segue o padrão TIFF (Tagged Image File Format 6). Os arquivos TIFF podem ser comprimidos ou armazenados como dados brutos. O comando a seguir lê a imagem e a armazena na lista uint8 original. O nome do arquivo vem primeiro, seguido pelo tipo de arquivo. original = imread('PIA02187.tif', 'TIFF');

5 6

Weeks 010.indd 345

Grupo Conjunto de Especialistas em Fotografia. (N.T.) Formato de Arquivo de Imagem Rotulado. (N.T.)

20/06/12 10:02

346

CAPÍTULO 10

Caso você não consiga localizar essa imagem, qualquer imagem apropriada em tons de cinza não comprimida servirá. Em seguida, salvamos a imagem de diversas maneiras diferentes. O primeiro parâmetro do comando imwrite é a variável que armazena os dados da imagem. Ele é seguido pelo nome do arquivo a ser criado e pelo tipo do formato de imagem a ser utilizado. Os dois últimos parâmetros especificam que o parâmetro opcional, qualidade, deve ser configurado com o número fornecido, até 100. Empregaremos o termo “imagem de qualidade 25” para nos referirmos à imagem armazenada na última linha do código a seguir. O número não indica de fato a qualidade, mas refere-se apenas ao parâmetro passado para a função imwrite. imwrite(original, imwrite(original, imwrite(original, imwrite(original,

'PIA_100.jpg', 'JPEG', 'Quality', 100); 'PIA_75.jpg', 'JPEG', 'Quality', 75); 'PIA_50.jpg', 'JPEG', 'Quality', 50); 'PIA_25.jpg', 'JPEG', 'Quality', 25);

Utilizaremos momentaneamente os tamanhos de arquivo para nos informarmos sobre a compressão. Mas não devemos comparar as imagens JPEG com a imagem TIFF, pois esses dois formatos diferentes variam. Nosso objetivo não é comparar os dois formatos, mas investigar a compressão e a qualidade de uma imagem. Assim, precisamos armazenar a imagem de uma forma em que não haja perdas. Note que a imagem ainda pode ser comprimida (armazenada de uma forma compacta), mas sem perda de informação. Felizmente, o armazenamento da imagem como um JPEG sem perdas é fácil, conforme demonstra o comando a seguir. imwrite(original, 'PIA_noloss.jpg', 'JPEG', 'Mode', 'lossless'); Podemos verificar os arquivos a partir do prompt do MATLAB. O comando ls (e o comando dir) apresenta o mesmo padrão de funcionamento que aquele em uma interface de linha de comando (tal como uma janela de terminal sob Linux, Unix e OS X, ou o programa command.exe sob Microsoft DOS ou Windows). Comandos correlatos tais como cd (change directory), pwd (print working directory) e mkdir (make directory) também são suportados. >> ls -l PIA* -rw-r--r-1 -rw-r--r-1 -rw-r--r-1 -rw-r--r-1 -rw-r--r-1 -rw-r--r-1

mweeks mweeks mweeks mweeks mweeks mweeks

staff staff staff staff staff staff

1446095 1074473 142951 209001 298521 978460

Feb Feb Feb Feb Feb Feb

17 17 17 17 17 17

18:41 18:45 18:45 18:45 18:45 18:45

PIA02187.tif PIA_100.jpg PIA_25.jpg PIA_50.jpg PIA_75.jpg PIA_noloss.jpg

Conforme vemos a partir da saída, os tamanhos de arquivo são: 1.4 megabytes para o original, 140 kilobytes para o comprimido com uma qualidade 25, 204 kilobytes e 291 kilobytes para os arquivos comprimidos com qualidades 50 e 75, respectivamente. Deve ficar claro que os números referentes à qualidade não indicam um percentual do tamanho de arquivo, caso contrário esperaríamos que o arquivo PIA_25.jpg tivesse metade do tamanho de PIA_50.jpg. A versão sem perdas ocupa 956 kilobytes de espaço. Estranhamente, vemos que a imagem comprimida com qualidade 100 na prática é 94 kilobytes maior que a imagem sem perdas! A explicação correta desse fato requer um exame detalhado do algoritmo JPEG inerente, o que foge ao escopo deste livro. É suficiente dizer que há ocasiões em que as etapas extras em um algoritmo de compressão produzem mais excesso do que economia. Agora que já temos a mesma imagem armazenada na forma de diversos arquivos comprimidos diferentes, vamos compará-los. Para tal, precisamos conhecer o conteúdo dos arquivos, de modo que iremos lê-los para matrizes separadas. compress_no_loss = imread('PIA_noloss.jpg', 'JPEG'); compress100 = imread('PIA_100.jpg', 'JPEG'); compress75 = imread('PIA_75.jpg', 'JPEG'); compress50 = imread('PIA_50.jpg', 'JPEG'); compress25 = imread('PIA_25.jpg', 'JPEG');

Weeks 010.indd 346

20/06/12 10:02

Aplicações

347

O traçado das imagens com o comando imshow mostrará que elas parecem todas iguais. Um observador meticuloso poderá ser capaz de apontar algumas diferenças, mas será impossível distinguir as versões comprimidas da original num relance. Para encontrarmos as diferenças, faremos uso do computador. diff25 = original - compress25; disp(sprintf('difference 25 = %d', ... sum(sum(abs(diff25))))); O segmento de código nos mostra como computamos a diferença entre o original e a versão comprimida de qualidade 25. Simplesmente subtraímos a matriz compress25 do original, encontramos o valor absoluto do resultado, somamos as colunas e, por fim, somamos as linhas. Podemos (e devemos) encontrar facilmente a soma das diferenças entre o original e cada uma das demais matrizes que armazenam a imagem. As diferenças brutas seguem abaixo, conforme informadas pelo MATLAB. Conforme o esperado, não há diferença entre a imagem compress_no_loss e a original. Se houvesse uma diferença, teria havido perdas! difference difference difference difference difference

no_loss = 0 100 = 60011 75 = 2174508 50 = 2944901 25 = 3962384

Uma forma de definir a taxa de compressão consiste em dividir o tamanho do sinal original pelo tamanho do sinal comprimido. Com essa definição, podemos encontrar a taxa de compressão comparando os tamanhos de arquivo com o tamanho do arquivo original. >> disp(sprintf('compress ratio q25 = %6.4f:1', 978460/142951 )); compress ratio q25 = 6.8447:1 Vemos que a taxa de compressão é de 6.8:1 para a imagem de qualidade 25, ou seja, o arquivo comprimido possui apenas 1 byte para cada 6.8 bytes do original. Temos 4.7:1 para a qualidade 50 e 3.3:1 para a qualidade 75. Conforme o esperado, as taxas de compressão ficam menores à medida que a qualidade aumenta. Como avaliar a qualidade das imagens resultantes? Dispomos de duas medidas comuns de qualidade de imagem. Entendemos como qualidade a fidelidade da imagem em comparação à original. Uma medida consiste no erro médio quadrático (root mean square error, ou RMSE). Para computá-lo, tomamos a soma das diferenças quadráticas e dividimos pelo número de pixels. Aqui computaremos isso para a imagem comprimida com qualidade 25. [rows, cols] = size(original); num_pixels = rows * cols; diff25 = original - compress25; mse = sum(sum(diff25.*diff25)) / num_pixels; rmse25 = sqrt(mse) Calculamos num_pixels como o número de linhas vezes o número de colunas. Para esta imagem, num_pixels = 1444256. Uma vez que cada localização da matriz armazena o valor de escala de cinza para um pixel, essa variável armazena o número total de pixels. Em seguida, calculamos a diferença entre o original e a versão comprimida. Na prática, já fizemos isso antes, mas queremos enfatizar como chegamos a diff25. A linha seguinte calcula o quadrado das diferenças, depois calcula a soma e, por fim, divide pelo número de pixels. Nós a chamamos de mse por se tratar do erro médio quadrático. A etapa final tira

Weeks 010.indd 347

20/06/12 10:02

348

CAPÍTULO 10

a raiz quadrada do MSE para gerar o RMSE. Os valores do RMSE produzidos pelo MATLAB seguem abaixo. RMSE with with with with with

compress_no_loss = 0.0000 compress100 = 0.2038 compress75 = 2.9732 compress50 = 3.9321 compress25 = 4.9551

O valor esperado para o RMSE de uma imagem ser perdas é zero. À medida que as compressões poupam cada vez mais espaço, percebemos a tendência de elevação do RMSE. Uma medida de qualidade correlata é a relação sinal ruído de pico (peak signal to noise ratio, ou PSNR). As unidades da PSNR são os decibéis, em homenagem a Alexander Graham Bell, daí a abreviação como dB. Uma vez que conheçamos o RMSE, poderemos calcular facilmente a PSNR. psnr25 = 20 * log10(255/rmse25); A constante 255 advém do máximo valor possível de nosso sinal (neste caso, um pixel). Como dispomos de uma imagem em tons de cinza, os pixels variam de 0 a 255. A saída do MATLAB, à medida que calculamos os valores da PSNR, segue abaixo. PSNR with with with with with

compress_no_loss = Inf compress100 = 61.9449 compress75 = 38.6664 compress50 = 36.2383 compress25 = 34.2297

O computador foi eficiente o bastante para imprimir Inf (infinito) em vez de uma mensagem de erro para a PSNR sem perda. Naturalmente, como a RMSE correspondente é igual a zero, dividimos por zero para obter a resposta infinita. Para as outras relações sinal/ruído de pico temos valores mais típicos, variando de 34 a 39 dB. A PSNR de 62 dB é bem alta, mas, por outro lado, a magnitude do erro no sinal compress100 é muito baixa. Para avaliar isso, vamos examinar quantos pixels foram efetivamente alterados na imagem compress100. >> sum(sum(round(diff100 ./ (diff100 + 1)))) ans = 60011 O código anterior requer uma explicação. A divisão ./ é aplicada em cada elemento individualmente. Portanto, cada elemento de diff100 é dividido por seu próprio valor mais 1. Você pode estar pensando “isso não produzirá sempre um resultado igual a 1?”, especialmente pelo fato de arredondarmos o resultado. Contudo, uma clara exceção notável ocorre quando o valor é igual a zero. Assim, geramos uma matriz de zeros e uns, onde cada um representa um número que não corresponde à imagem original. Portanto, a soma dessa matriz nos revela quantos pixels eram diferentes entre a imagem original e aquela armazenada com qualidade 100. Vimos anteriormente que a soma das diferenças para aquela imagem correspondia ao mesmo número que o do código anterior (60011). Isso significa que a maior diferença era de apenas 1. Verificamos a seguir essa afirmação. >> max(max(diff100)) ans = 1

Weeks 010.indd 348

20/06/12 10:02

Aplicações

349

Sabemos quantos pixels (60011) na reconstrução são diferentes do original. O quanto esse número é significativo? Podemos descobrir comparando com o número total de pixels. >> sum(sum(round(diff100 ./ (diff100 + 1)))) / num_pixels ans = 0.0416 Somente 4.16% dos pixels possuem um valor diferente em relação ao original e, portanto, a diferença máxima é apenas 1. Compare isso à imagem armazenada com qualidade 25, onde 43% dos pixels possuem um valor diferente em relação ao original, com uma grande diferença máxima. Para a escala de cinza, a diferença de 81 em um valor de pixel é muito significativa. Em seguida, utilizamos o MATLAB para verificar a imagem com qualidade 25. Dos pixels alterados em relação ao original, a mudança média corresponde a 6. >> % numero de pixels alterados >> sum(sum(round(diff25 ./ (diff25 + 1)))) ans = 619209 >> % diferenca media >> sum(sum(diff25)) / 619209 ans = 6.3991 >> % diferenca maxima >> max(max(diff25)) ans = 81 >> % numero de pixels alterados, percentual >> 100 * sum(sum(round(diff25 ./ (diff25 + 1)))) / num_pixels ans = 42.8739 Mas como são essas imagens? São boas o bastante? Trata-se, naturalmente, de uma avaliação subjetiva, que variará de pessoa para pessoa. Com frequência, devemos encontrar um equilíbrio entre o tamanho da imagem e a qualidade perceptível. Para maiores informações sobre compressão, consulte o livro de S. W. Wu Additive Vector Decoding of Transform Coded Images [48]. Outra fonte de consulta, especialmente para a compressão de imagens degradadas, é o livro Lossy Compression of Noisy Images, de O. K. Al-Shaykh e R. M. Mersereau [49].

10.10.2

Compressão de Imagem por Conta Própria

Vamos tentar nós mesmos comprimir uma imagem. Conforme dissemos anteriormente, há três etapas a serem cumpridas: transformação, quantização e codificação de entropia. Primeiramente, precisamos de uma imagem de teste. Nós a lemos do arquivo e a chamamos de original. original = imread('dog256x256.gif', 'GIF');

Weeks 010.indd 349

20/06/12 10:02

350

CAPÍTULO 10

Utilizaremos a DWT para nossa transformada, especificamente a DWT bidimensional com os coeficientes db4, para três oitavas. No código a seguir calculamos as três oitavas, uma de cada vez. As letras maiúsculas caracterizam a saída da DWT, enquanto o número denota a oitava. Por exemplo, B2 contém os dados do detalhe B na oitava 2. Como não precisaremos de A1 e A2 quando tivermos terminado, descartaremos essas variáveis. wavelet = 'db4'; [A1, B1, C1, D1] = dwt2(double(original), wavelet); % octave 2 [A2, B2, C2, D2] = dwt2(A1, wavelet); % octave 3 [A3, B3, C3, D3] = dwt2(A2, wavelet); clear A1 A2 Agora prosseguiremos para a etapa de quantização, na qual utilizaremos uma simples função de quantização. Na verdade, a função possui apenas uma linha! Ela toma a matriz de entrada, divide cada elemento por um fator e, em seguida, arredonda o resultado e o multiplica pelo fator. À primeira vista pode parecer que isso nada faz, mas na realidade reduz a precisão dos dados. Suponha, por exemplo, que passemos um valor escalar de 31.3, com um fator 2. Se dividirmos o valor pelo fator, o resultado será 15.65, que arredondamos em seguida para 16. Por fim, multiplicamos 16 pelo fator, para encontrarmos o número 32. Isso não apenas elimina a parte fracionária como também retorna sempre um número par para o fator 2. Valores de fator mais alto significam que a função encontra uma aproximação menos precisa para os valores de entrada. function out = simple_quantize(in, factor) out = factor*round(in/factor); Para cada um dos subsinais B1, B2 e assim por diante, utilizamos essa simples função de quantização para encontrar aproximações. Aqui utilizaremos um fator um tanto conservador, de valor 8, que resultará em baixa compressão mas uma alta PSNR. Em seguida apresentamos um exemplo que mostra a simples função de quantização utilizada em B1 para gerar B1-q, a versão quantizada de B1. factor = 8; B1_q = simple_quantize(B1, factor); A terceira etapa consiste na codificação da entropia. Para simplificar as coisas, converteremos os dados das matrizes bidimensionais para listas unidimensionais. A função flatten faz exatamente isso, colocando o número de linhas e colunas nas duas primeiras posições da lista e acrescentando à matriz linha por linha. Mais tarde utilizaremos a função unflatten correspondente para revertê-la (ambas inclusas no CD-ROM). Tal como mostra o código a seguir, utilizamos _flat para monitorar essa versão de B1. Naturalmente, repetimos isso para cada uma dos subsinais “achatados”.7 b1_flat = flatten(B1_q); Vamos tratar agora da real codificação de entropia. Inclusas no CD-ROM encontram-se as funções huffman e undo_huffman, que executam a codificação e a decodificação de entropia, respectivamente. A codificação de Huffman é um famoso algoritmo de codificação de entropia cujo nome é uma homenagem ao seu inventor. A partir dos dados de entrada ela gera uma lista de valores únicos e conta em seguida quantas vezes esses valores aparecem nos dados. A partir dessa lista de popularidade, ela gera uma árvore binária com os valores únicos como suas folhas. Aqueles que aparecem com frequência conseguem uma posição próxima à raiz. A codificação de um valor acaba sendo, portanto, simplesmente uma questão de encontrá-lo na árvore. Suponha que o valor que desejamos codificar ocupe um nó de folha dois ramos

7

Weeks 010.indd 350

Flatten = achatar, aplainar. (N.T.)

20/06/12 10:02

Aplicações

351

esquerdos a partir da raiz e depois um ramo direito. Codificamos zeros para ramos esquerdos e uns para os direitos, de modo que a codificação seria 001. Quanto mais um número aparecer nos dados, menos bits serão utilizados para representá-lo. Os números que aparecem com menos frequência são armazenados com cadeias de bits mais longas. Novamente, utilizaremos o subsinal B1 modificado em um código de exemplo: b1_huff = huffman(b1_flat); Note que a codificação de entropia requer um trabalho adicional para funcionar. Se utilizarmos informação para criar os códigos, precisaremos armazenar essa informação para poder recriar os códigos mais tarde. A lista b1_huff contém esses dados em excesso, bem como os dados codificados. Esperamos que o resultado codificado dessa entropia (b1_huff) seja menor que a versão codificada sem entropia (bi_flat), caso contrário não haveria sentido em produzi-lo. Entretanto, um sinal sem repetição de valores geraria um resultado codificado de entropia maior. A seguir apresentamos um exemplo extremo disso, onde precisamos de nove vezes mais espaço: >> r = 1:1000; >> q = huffman(r); >> length(q) ans = 9254 Isso explica como um sinal comprimido pode ocupar mais espaço do que a versão não comprimida. Em termos realistas, nossos sinais terão mais redundância do que podemos usar a nosso favor. Agora que a etapa de quantização deve estar mais clara, nós a utilizaremos para reduzir o número de valores que precisamos armazenar. Em seguida, vemos como a versão codificada de entropia de B1 (depois) requer somente um quarto do espaço em comparação com a versão não codificada (antes). Este é um exemplo muito mais típico. >> length(b1_flat)

% antes

ans = 17163 >> length(b1_huff)

% depois

ans = 4334 Tudo que precisamos fazer agora é armazenar nossos subsinais em um arquivo. A função writeAfile (inclusa no site da LTC Editora) foi criada para esse propósito. O arquivo comprimido ocupa somente 23.687 bytes. Compare isso ao tamanho original de 65.536 bytes, ou um byte para 2562 pixels. Isso corresponde a apenas 36% do tamanho original. Note que não consideramos o tamanho do arquivo da imagem original, pois ela está armazenada como uma imagem GIF codificada. O RMSE para a imagem comprimida é 1.6, com uma PSNR de 44 dB. Visualmente, a comparação da imagem resultante com o original é favorável. Assim, a imagem de teste foi comprimida com sucesso para cerca de um terço de seu tamanho original. Nesta seção, aprendemos os fundamentos da compressão e como as etapas de transformação, quantização e codificação de entropia nos permitem armazenar dados em uma forma aproximada, porém eficiente. Examine os arquivos de projeto compress_test.m e uncompress_test.m, disponíveis no site da LTC Editora. O primeiro programa abrange o que estudamos anteriormente, enquanto o último reverte as operações em cada etapa. Juntos, eles mostram que o algoritmo de compressão com perdas que exploramos aqui faz um bom trabalho de armazenamento de uma imagem com eficiência. Encorajamos o leitor a experimentar esses programas e a alterar os parâmetros de fator e wavelet para constatar seus efeitos na compressão.

Weeks 010.indd 351

20/06/12 10:02

352

CAPÍTULO 10

RESUMO . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Este capítulo apresenta diversas aplicações de processamento digital de sinais e de MATLAB. Começamos pelos conceitos básicos de manipulação de som, incluindo gravação, leitura e escrita em disco, além da reprodução. Em seguida, vimos como as imagens podem ser criadas, salvas e carregadas, bem como reduzidas em tamanho. Depois disso, aplicamos imagens como entrada na DWT e exibimos os resultados desta transformada como uma imagem. Examinamos em seguida alguns programas presentes no site da LTC Editora que executam a DWT e desfazem a transformada, preservando, contudo, as contribuições de cada canal. Esses programas são bons para análise, tal como a localização de bordas dentro de uma imagem. Demonstramos a programação recursiva com o quebra-cabeça Sudoku e descobrimos que um quebra-cabeça mal construído poderia ter múltiplas soluções. Em seguida, examinamos um programa de amostragem de som que responde com um traçado de frequência a qualquer som nas proximidades. Analisamos o projeto de um filtro e mostramos como nós mesmos poderíamos especificar os coeficientes de filtro. Por fim, este capítulo inclui uma breve introdução à compressão.

EXERCÍCIOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1. Suponha que a variável x contenha informações de som, armazenadas como valores double (dobrados). Que efeito o comando sound(x*.7) produz? Que efeito o comando sound(x*1.1) produz? (Talvez você tenha que especificar a taxa de amostragem como um segundo parâmetro para o comando sound.) 2. Dado um pequeno arquivo de som, mostre como você pode reproduzi-lo 10 vezes com uma taxa de amostragem diferente a cada vez. 3. Como você pode concatenar duas listas de som de modo a reproduzi-las uma após a outra? O que acontecerá se elas tiverem dois canais cada uma? 4. Fornecido um arquivo de som, escreva um código MATLAB que o reproduza de trás para a frente. 5. Converta um sinal sonoro para o domínio da frequência, desloque o sinal copiando os dados no domínio da frequência para novos índices e converta-o de volta. Você pode conseguir isso mudando a frequência de amostragem? O que acontecerá se você reverter a ordem dos índices antes de converter de volta? 6. Crie um programa que reverta uma imagem em tons de cinza. Por exemplo, a reversão da imagem de uma caixa branca em um fundo preto resultaria em uma caixa preta em um fundo branco. O que aconteceria se a imagem estivesse em cores? 7. Vimos anteriormente um código que gira uma imagem no sentido horário. Modifique-o para girar a imagem no sentido anti-horário. 8. Quando o solucionador do Sudoku é executado, o quebra-cabeça apresenta diferentes estados à medida que os palpites substituem as casas em branco. Esses estados são armazenados na pilha, uma estrutura interna de dados computacionais. Qual o valor do quebra-cabeça após a execução do programa? Como poderíamos armazenar os estados do quebra-cabeça de uma forma que pudéssemos acessá-los mais tarde? 9. O que aconteceria se a função de busca do solucionador do Sudoku não incluísse a palavra chave function em sua definição? 10. Examine o código do solucionador do Sudoku para encontrar o número de operações de comparação envolvidas. Se dispuséssemos de uma versão hexadecimal do quebra-cabeça Sudoku, como o número de operações mudaria? 11. Durante a utilização da função window para projetar coeficientes de filtro, muitas janelas são suportadas. Faça experiências com as janelas gausswin, hamming, rectwin e triang para determinar que janela produz os melhores resultados para um filtro FIR passa-baixo. 12. Vimos que a inversão alternada dos sinais para os coeficientes de um filtro passa-baixo tem o efeito de transformá-lo em um filtro passa-alto. A inversão alternada imita a multiplicação de cada valor por uma senoide de alta frequência. Escreve um código que altere um conjunto de coeficientes de filtro multiplicando-os por uma senoide mais lenta. Que efeito você observa?

Weeks 010.indd 352

20/06/12 10:02

Aplicações

353

13. Encontrar o número de valores distintos constitui basicamente uma contagem. Uma forma de fazê-lo na forma de um algoritmo consiste em ordenar os valores e eliminar quaisquer duplicatas – o número de valores restantes corresponde ao número de valores distintos. Por exemplo, na série {7, 3, 5, 1, 3, 3, 5, 4, 1, 7, 4, 5} há somente cinco valores distintos: {1, 3, 4, 5, 7}. A mais próxima potência de 2 maior que (ou igual a) isso é 8, ou 23. Assim, podemos atribuir a cada valor um código diferente de 3 bits. O que queremos fazer é armazenar um sinal em menos espaço. Ou seja, poderíamos representar estes valores com 3 bits cada. Esta não é a forma mais eficiente de armazenar estes valores, mas seu propósito é dar a você uma ideia de como a compressão funciona. (A codificação de Huffman é mais eficiente.) Suponha que tenhamos o seguinte sinal de exemplo: x[n] = {0, −1, 9, −1, 9, 9, 0, 3, −1, 3, 0, 0}. Ao armazenarmos estes valores como inteiros, provavelmente utilizaríamos 16 bits para armazenar cada valor, para um total de 192 bits. Isso, no entanto, seria um desperdício em termos da quantidade de espaço. (a) Quantos valores distintos há no sinal de exemplo anterior, x[n]? (b) Decida que padrões de bits devem representar cada valor distinto (formando uma tabela de pesquisa). (c) Mostre como o sinal de exemplo completo anterior (x[n]) se pareceria, codificado. Lembre-se de que também queremos ser capazes de desfazer isso – fornecidos o padrão de bits e a tabela de pesquisa, devemos ser capazes de obter x[n] de volta. (d) Quantos bits o sinal codificado ocupa? E se você incluísse a tabela de codificação?

PROJETO . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Comprima um arquivo de texto utilizando a codificação de Huffman. É possível obter uma reconstrução perfeita? O que acontecerá se você executar a transformada aditiva/subtrativa nos dados antes de utilizar a codificação de entropia? Um exemplo de solução é fornecido no CD-ROM, como project10.m. Tente resolver este problema por conta própria, antes de ler o texto a seguir.

Existem muitos arquivos de texto diferentes que podemos utilizar para testar a compressão. Os scripts MATLAB são arquivos de texto, assim como algumas codificações de imagem. Quando você envia uma figura por email, por exemplo, o computador envia os dados correspondentes em uma codificação de 64 bits consistindo em letras minúsculas, letras maiúsculas, números e alguns outros símbolos. Outro exemplo é a imagem rect_triangle.ppm inclusa no CD-ROM, que pode ser alterada com um editor de textos. Utilizaremos esta imagem como nosso arquivo de teste. Vamos recorrer aos programas apresentados neste capítulo. Utilizaremos readAfile2 para ler o arquivo original, huffman para calcular a codificação de Huffman e writeAfile2 para gravar o resultado em um novo arquivo. original = readAfile2(filename); compressed = huffman(original); writeAfile2(compressedFilename, compressed); Tal como mostra o código acima, as funções definidas anteriormente nos permitem obter facilmente a compressão. Em seguida, lemos os dados do arquivo comprimido e os expandimos de volta. Novamente, as funções definidas neste capítulo simplificam esta tarefa. data = readAfile2(compressedFilename); uncompressed = undo_huffman(data);

Weeks 010.indd 353

20/06/12 10:02

354

CAPÍTULO 10

A seguir, comparamos os dados não comprimidos com o original. err = sum(abs(original - uncompressed)); Esperamos (e exigimos) que este erro seja zero, uma vez que utilizamos compressão sem perdas. Agora que podemos comprimir um arquivo e verificar que sua descompressão nos devolve uma cópia exata do mesmo, podemos fazer experimentos com o papel da transformada na compressão. Executamos a transformada Aditiva/Subtrativa nos dados e os comprimimos em seguida com codificação de Huffman. Depois, nós os gravamos em um novo arquivo. [data_plus, data_minus] = plusminus(original); compressed = huffman([data_plus, data_minus]); writeAfile2(PMcompressedFilename, compressed); A transformada retorna duas listas, contendo os resultados aditivos e os resultados subtrativos. Nós os tratamos como uma longa lista ao passa-los para a função huffman. Neste caso, não executamos qualquer quantização, pois desejamos uma reconstrução perfeita. Para este arquivo de entrada, a transformada efetivamente não ajuda, pois a codificação de Huffman retorna mais dados do que o fez com o original. A etapa seguinte é desfazer a transformada. Lemos os dados comprimidos, desfazemos a codificação de Huffman, decompomos a longa lista em duas listas e calculamos a transformada inversa. O código a seguir exibe as duas primeiras destas operações. data = readAfile2(PMcompressedFilename); uncomp = undo_huffman(data);

% leia dados comprimidos % desfaca a codificacao de Huffman

Abaixo, encontramos o ponto intermediário dos dados não comprimidos (uncomp) e os dividimos em duas listas. Elas correspondem a data_plus e data_minus, e espera-se que sejam cópias exatas. half = floor(length(uncomp)/2); uncomp1 = uncomp(1:half); uncomp2 = uncomp(half+1:length(uncomp));

% encontre o ponto intermediario % obtenha a primeira metade % obtenha a segunda metade

Agora podemos calcular a transformada inversa. PMuncompressed = undo_plusminus(uncomp1, uncomp2); Existe uma chance de que o resultado da transformada inversa seja um valor maior que o original, de modo que podemos verificar isso e corrigir a extensão caso seja necessário. Por fim, encontramos o erro entre o original e a versão do mesmo que foi transformada, comprimida, descomprimida e transformada inversamente. Esperamos que ele seja zero e que o computador verifique isso. O objetivo deste projeto é mostrar como uma transformada pode ser utilizada com compressão. Encorajamos o leitor a experimentar outras transformadas e outros arquivos de entrada, bem como experimentar os efeitos da quantização.

Weeks 010.indd 354

20/06/12 10:02

Constantes e Variáveis Utilizadas Neste Livro

A.1

A

Constantes

A letra grega pi é representada por ␲ e equivale a ⬇ 3.14159 Constante de Euler: e ⬇ 2.71828

A.2

Variáveis

␾ é uma variável utilizada para representar algum ângulo. Frequentemente a utilizamos para representar o ângulo de fase em uma senoide variante no tempo. Ele é tipicamente fornecido em radianos – por exemplo, ␾ = ␲/6. ␪ é uma variável utilizada para representar algum ângulo. Ela frequentemente é utilizada como o argumento de uma função seno ou cosseno – por exemplo, cos(␪). ␴ é utilizada em estatística – por exemplo, ␴2 representa a variância. ␻ é outra variável utilizada para representar algum ângulo. Ela também é empregada para representar uma frequência, ou, mais precisamente, uma frequência em radianos. Nesse sentido, ␻ = 2␲f. Ela possui unidades de radianos/segundos. a representa a amplitude, utilizada com uma senoide. Em alguns livros especializados em wavelet essa variável é empregada no lugar de s (consulte a variável s). b é utilizada como uma variável de exemplo. Alguns livros especializados em wavelet a empregam no lugar de u (consulte a variável u). c é utilizada como uma variável ou constante de exemplo. Quando utilizada no contexto da transformada de wavelet Chapéu Mexicano, ela especifica uma constante de normalização. f representa uma frequência, fornecida em Hertz. g representa um coeficiente de filtro FIR, aparecendo com wavelets uma vez que h já se encontra em uso. Tipicamente, g corresponde ao filtro passa-alto de um par de filtros. h representa a resposta ao impulso unitário. Para os filtros FIR, h será a mesma coisa que os coeficientes de filtro. No caso de um par de filtros, h corresponde ao filtro passa-baixo. i é frequentemente utilizada como um índice. O MATLAB a entende como sido redefinida naquela sessão. j representa tipicamente

a menos que ela tenha

exceto no caso de wavelets, quando é utilizada como um índice. 355

Weeks Apendice A.indd 355

20/06/12 10:08

356

APÊNDICE A

J representa o limite superior para j quando se trata de wavelets. k representa um inteiro. K é utilizada como um limite, com o índice k. m é utilizada como um índice. Ela sempre é um inteiro. Normalmente, m é empregada quando n já se encontra em uso para outra coisa. n é utilizada como um índice. Ela sempre é um inteiro e, às vezes, pode ser até negativa. N é utilizada como um limite, geralmente para o tamanho de uma lista. É comum vê-la sendo utilizada com n, tal como n = 1 a N, ou n = 0, . . . , N − 1. s é utilizada com a transformada contínua de wavelet para escalonamento. t representa o tempo. Trata-se de uma variável contínua. u é utilizada com a transformada contínua de wavelet para o deslocamento de uma função. w geralmente se refere a um sinal, sendo frequentemente utilizada quando x e y já estão definidas. Não a confunda com ␻ (a letra grega ômega minúscula), pois possuem significados diferentes. W e Wh referem-se às saídas passa-baixo e passa-alto da transformada de wavelet, respectivamente. x é utilizada por toda parte para representar um sinal, frequentemente uma entrada. Ela pode ser contínua, tal como x(t), ou pode ser discreta, como x[n]. X tanto pode representar a transformada z quanto a transformada de Fourier de x. y é utilizada por toda parte para representar um sinal, frequentemente uma saída. Ela pode ser contínua, tal como y(t), ou pode ser discreta, como y[n]. z geralmente representa uma variável complexa, tal como na transformada z. Ela também é utilizada como o nome para um sinal, caso x e y já estejam em uso.

Sempre que houver uma letra em caixa baixa (minúscula), a mesma letra em caixa alta (maiúscula) representará o limite superior, ou a informação no domínio da frequência, dependendo de a letra minúscula ser um índice ou um sinal. Por exemplo, Y[m] seria considerada a transformada de Fourier (ou a transformada z) do sinal y[n], em que m e n são índices inteiros, provavelmente variando de 0 a N − 1.

Weeks Apendice A.indd 356

20/06/12 10:08

Constantes e Variáveis Utilizadas Neste Livro

Tabela A.1

Nome alfa beta gama delta épsilon zeta eta teta iota capa lambda mi ni si ômicron pi rô sigma tau ípsilon fi qui psi ômega

A.3

Maiúsculas 〈 〉 ⌫ ⌬ ⌭ ⌮ ⌯ ⌰ ⌱ ⌲ ⌳ ⌴ ⌵ ⌶ ⌷ ⌸ ⌹ ⌺ ⌻ Y, ⌼ ⌽ ⌾ ⌿ ⍀

357

Alfabeto grego. Minúsculas ␣ ␤ ␥ ␦ ⑀, ␧ ␨ ␩ ␪, ␽ ␫ ␬ ␭ ␮ ␯ ␰ ␱ ␲, ␼ ␳, ␴ ␶ ␷ ␾, ␸ ␹ ␺ ␻

Equivalentes a b g d e z e th i k l m n x o p r s t u ph ch ps o

Símbolos Comuns em Livros sobre DSP

僆 é utilizado para indicar “elemento de”. Por exemplo, b 僆 {0, 1} significa que o valor de b deve estar dentro da série, ou seja, b pode ser 0 ou 1.  é o conjunto de todos os números naturais, começando por 1. Ou seja, {1, 2, 3, . . . }.  é o conjunto de todos os números naturais, incluindo o zero. A expressão i 僆  equivale a definir unsigned int i; em um programa C/C++ (exceto pelo fato de alguns inteiros serem muito grandes para a faixa definida pela linguagem de programação).  é o conjunto de todos os números reais. Por exemplo, a expressão a 僆  significa que a é um número real. Isso equivale float a; em C/C++, exceto pelo fato de floats (variáveis de ponto flutuante) possuírem algumas limitações que as variáveis matemáticas abstratas não possuem, tais como os limites na precisão. n é o conjunto de todos os vetores de números reais de extensão n. Ele é semelhante a todas as listas de valores reais possíveis. Uma forma de pensar em c 僆 n consiste em pensar em c como se ele fosse definido como uma lista real – por exemplo, float c[n];. (Note os limites mencionados anteriormente.)

Weeks Apendice A.indd 357

20/06/12 10:08

Weeks Apendice A.indd 358

20/06/12 10:08

Equações

B.1

B

Convolução

Convolução:

A convolução possui as seguintes propriedades [13]: comutativa: a * b = b * a; associativa: a * (b * c) = (a * b) * c; distributiva: a * (b + c) = a * b + a * c.

B.2

Correlação

Estas fórmulas fornecem uma estimativa da correlação e são fornecidas para sinais de valores reais. Se os sinais forem complexos, utilize o conjugado complexo. Autocovariância:

Covariância cruzada:

Autocorrelação:

Correlação cruzada:

359

Weeks Apendice B.indd 359

20/06/12 10:14

360

B.3

APÊNDICE B

Fórmula de Euler

A equação de Euler:

A equação de Euler também funciona para um expoente positivo, ou seja,

Quando por acaso o ângulo em questão é ␲,

A fórmula inversa de Euler:

Além disso, podemos expressar isso em termos da função seno.

B.4 B.4.1

Transformada de Fourier Transformada Contínua de Fourier

A transformada contínua de Fourier (CFT):

A transformada contínua de Fourier inversa:

B.4.2

Transformada Discreta de Fourier

A transformada discreta de Fourier (DFT):

Forma alternativa:

em que m = 0, . . . , N − 1.

Weeks Apendice B.indd 360

20/06/12 10:14

Equações

B.4.3

361

Transformada Discreta de Fourier Inversa

A transformada discreta de Fourier inversa (IDFT):

Forma alternativa:

em que n = 0, . . . , N − 1.

B.4.4

Magnitudes, Fases e Frequências de Análise

A magnitude de saídas DFT:

A fase de saídas DFT: Se Xreal[m] > 0,

senão

As frequências de análise são examinadas em Understanding Digital Signal Processing, de Richard Lyons [12]:

B.5

Aproximação de Integral

Uma integral pode ser aproximada da seguinte maneira:

em que

Weeks Apendice B.indd 361

20/06/12 10:14

362

B.6

APÊNDICE B

Amostragem

O critério de Nyquist:

amostragem: x(t) torna-se x(nTs) = x[n] A amostragem de banda limitada, extraída de Understanding Digital Signal Processing, de Richard Lyons [12]:

B.7

Estatística

A variância de uma variável aleatória, de Ronald E. Walpole e Raymond H. Myers, Probability and Statistics for Engineers and Scientists, Third Edition [14]:

em que ␮ é a média. A variância da amostra (também de Walpole e Myers) [14]:

O erro médio quadrático (MSE, de mean square error) entre uma imagem e sua reconstrução (possivelmente após uma compressão) é:

A raiz quadrada do erro médio quadrático (RMSE, de root mean square error) é calculada por meio das seguintes fórmulas:

Para dados bidimensionais tais como imagens:

Weeks Apendice B.indd 362

20/06/12 10:14

Equações

363

em que MSE significa erro médio quadrático. A equação da relação sinal/ruído (SNR, de signal to noise ratio) segue abaixo.

em que ␴x é a média quadrática do sinal de entrada [50]. Uma fórmula correlata geralmente utilizada em processamento de imagem é a relação sinal ruído de pico (PSNR, de peak signal to noise ratio).

onde MáxValorPossível resulta da codificação – por exemplo, 255 para escala de cinza [51].

B.8

Identidades Trigonométricas e Outras Notas Matemáticas

se k é um inteiro, cos(␪ + 2␲k) = cos (␪).

De Elements of Calculus with Analytic Geometry, de autoria de Earl William Swokowski [22], temos:

De Exploring Numerical Methods: An Introduction to Scientific Computing Using MATLAB, de Peter Linz e Richard L. C. Wang [11], temos:

Weeks Apendice B.indd 363

20/06/12 10:14

364

APÊNDICE B

De cartesiano para polar:

Se Se Se Se

x x x x

e y forem ambos negativos, acrescente ␲ a ␪. for negativo, mas y for positivo, subtraia ␲ de ␪. for zero e y for negativo, então ␪ = −␲/2. for zero e y for positivo, então ␪ = ␲/2. De polar para cartesiano:

Fórmula quadrática:

Obs.: mas SOMENTE quando tanto ␣ quanto ␤ são inteiros positivos. Trata-se de um erro comum que ocorre em alguns problemas de DSP.

B.9 B.9.1

Transformada de Wavelet Transformada Discreta de Wavelet (DWT)

A função de escalonamento da DWT:

ou

A função de Wavelet da DWT:

ou

Weeks Apendice B.indd 364

20/06/12 10:14

Equações

B.9.2

365

Transformada Contínua de Wavelet

Função Chapéu Mexicano (um exemplo de wavelet):

O Chapéu Mexicano pode ser normalizado multiplicando-se pelo valor c, como a seguir.

A função de wavelet deslocada e escalonada:

Transformada contínua de wavelet:

B.10

Transformada z

A transformada de Laplace:

em que s = ␴ + j␻ Observe como essa integral começa em 0, em vez de –infinito. Aqui consideramos que o sinal inicia em t = 0. A transformada z de um sinal discreto x[n] é

em que

Função de transferência:

Weeks Apendice B.indd 365

20/06/12 10:14

Weeks Apendice B.indd 366

20/06/12 10:14

Ideias de Projetos de DSP

C

Algumas ideias para projetos de fim de semestre na área de processamento digital de sinais são apresentadas aqui. Alguns dos projetos são mais complexos do que outros e exigirão mais tempo. Além disso, esses projetos podem ser executados de forma mais interessante aplicando-se técnicas avançadas como redes neurais, modelos ocultos de Markov, filtragem adaptativa e outras ferramentas que estão além do escopo deste livro. Durante o período de alguns meses, os projetos não serão de vulto. Atalhos devem ser incluídos na proposta do projeto original e confirmados em um relatório final. Por exemplo, o número de sinais de teste deve ser muito pequeno e o efeito nos resultados do projeto deve ser discutido. 1. Marca d’água de imagem Pense numa forma de inserir uma marca d’água simples em uma imagem que seja invisível para um observador humano. 2. Filtragem de ruído Crie um programa que filtre um sinal ruidoso, tal como a limpeza de um sinal referente a um discurso gravado em uma sala repleta de conversas paralelas. 3. Compressão Dado um sinal de música, armazene a informação de ambos os canais da forma mais eficiente possível. O que acontecerá se o seu programa subtrair um canal do outro? E se você utilizar a amostra anterior como o valor esperado para a amostra seguinte e armazenar somente a diferença? 4. Diferenciador musical Estude algumas canções que representem diferentes tipos musicais e escreva um programa que faça a distinção entre elas. É razoável, por exemplo, esperar que o estilo rap contenha mais energia na faixa de baixa frequência (baixos) em comparação à música country. 5. Conversor de cores Escreva um programa que mapeie automaticamente todas as cores em uma imagem colorida para valores diferentes. O programa pode, por exemplo, converter todas as cores para tons pastéis. Ou pode converter todas as cores para tons de cinza, exceto para uma – por exemplo, destacando o verde. 6. Remoção de olhos vermelhos Frequentemente as fotos mostram pessoas com olhos vermelhos, devido ao flash. Um projeto interessante seria detectar e remover automaticamente os olhos vermelhos. Para facilitar as coisas, considere que as áreas relativas aos olhos sejam fornecidas pelo usuário. 7. Detector de tanques Encontre um modo de encontrar um objeto específico em uma imagem, tal como um tanque militar. Seu programa deve retornar um flag indicando se um tanque está ou não presente e, em caso afirmativo, a posição do mesmo na imagem. Este pode ser um projeto bastante complexo, de modo que talvez você queira simplificar as coisas considerando que o tanque consiste em um contorno preto sobre um fundo branco. Seu programa deve ser capaz de encontrar o tanque independentemente de sua posição na imagem, denominada translação. Eis algumas variantes desta ideia (cada uma das quais deve constituir um projeto independente): detectar o objeto a despeito da rotação, detectar o objeto a despeito da obstrução (por exemplo, se ele for parcialmente encoberto por outro objeto) e detectar o objeto a despeito da escala (menor ou maior). 367

Weeks Apendice C.indd 367

20/06/12 10:17

368

APÊNDICE C

8. Sistema de identificação de gatos Muitos donos de gatos conseguem reconhecer seus animais pelos seus miados. Desenvolva um sistema que tente identificar corretamente um gato em particular entre diversas gravações de gatos diferentes. 9. Tradutor de cães Os donos de cães muitas vezes sabem o que seus animais desejam pela forma como latem ou ganem. Desenvolva um sistema que consiga distinguir, entre os ruídos que seu cão emite, quando ele vê outro cão, quando está com fome, quando alguém bate à porta, quando ele quer sair etc. 10. Separação de objetos Crie um programa que separe uma imagem em imagem de primeiro plano e imagem de plano de fundo. Por exemplo, tire uma foto da face de uma pessoa e armazene-a como uma imagem em escala de cinza. Seu programa deve ser capaz de criar uma nova imagem a partir dela somente com a face da pessoa em um fundo branco. 11. Detector de música Utilize o MATLAB e um microfone para gravar uma fração de segundo de som e analisá-lo para determinar se o sinal se refere a uma música, discurso ou simplesmente silêncio. Você pode ligar um rádio perto do microfone e verificar quão bem o sistema funciona. 12. Assobio como uma forma de controle remoto Utilize o MATLAB e um microfone para que o computador grave um sinal sonoro e o analise, procurando um som de assobio, e o repita em um loop. Se o assobio for detectado, gere uma saída indicando o fato. Um sistema avançado deve ser capaz de detectar diferentes assobios emitidos pelo mesmo usuário e responder adequadamente. Por exemplo, o assobio de uma pessoa poderia fazer com que o computador anunciasse a hora, enquanto outro assobio produziria o anúncio do dia. 13. Previsão do tempo Utilize a transformada de Fourier das temperaturas do mês passado para tentar prever qual será a temperatura para os próximos dias. Você pode utilizar os erros das previsões anteriores para produzir uma previsão mais precisa? Além da temperatura, uma tarefa mais complexa consiste em utilizar leituras de pressão barométrica, temperatura e direção/velocidade do vento para prever outros padrões de tempo, tais como tempestades. 14. Rastreador de bola de basquete Dado um videoclipe de um jogo de basquete, tente rastrear automaticamente a bola. Isso deve funcionar com qualquer tipo de jogo. 15. Compressão de imagem Desenvolva seu próprio programa de compressão de imagem sem perdas e avalie seu funcionamento.

Weeks Apendice C.indd 368

20/06/12 10:17

Conteúdo dos Exemplos Disponíveis no Site da LTC

D

O material disponível no site da LTC Editora contém os exemplos de MATLAB encontrados neste texto. Também estão inclusas as figuras presentes em todos os capítulos deste livro. Para cada capítulo, existe um projeto de exemplo. Para uma rápida introdução de cada um, consulte os arquivos “README” – por exemplo, “capítulo1.README” para o primeiro capítulo.

369

Weeks Apendice D.indd 369

20/06/12 10:20

Weeks Apendice D.indd 370

20/06/12 10:20

Respostas para Exercícios Selecionados

E

Capítulo 1 1. O que é um sinal? Resposta: Um sinal é um fenômeno mensurável e variável – frequentemente uma quantidade física que varia com o tempo.

3. O que é uma transformada? Resposta: Uma transformada é a operação que o sistema executa.

5. O que x(t) implica? Resposta: Que o sinal x é indexado por t. Que o sinal x é contínuo. Que o índice t também é contínuo. Que x(t) representa um sinal analógico.

7. Represente 75.625 como um número binário de ponto fixo. Resposta: Primeiramente, converta 75 em binário.

Leia os restos de baixo para cima. Acrescente um 0 à esquerda, para que não o confundamos com um número negativo. Deste modo, 75 em binário é igual a 01001011. Para encontrarmos .625 em binário, multiplicamos por 2 e mantemos a parte inteira.

Leia as partes inteiras de cima para baixo. O resultado é .101 em binário, de modo que a resposta é 01001011.101 em binário. Uma vez que não especificamos quantos bits esse número ocupa, o deixaremos como está, sem acrescentar zeros (ou truncá-lo).

9. Suponha que tenhamos um número de ponto fixo com o padrão bbb.bbbbb, onde b representa um bit. Se considerarmos somente números positivos, que faixa de valores (decimais) podemos armazenar? Quantos valores diferentes um número armazenado com essa precisão pode ter? Resposta: Os números variam de 000.00000 a 111.11111. O mínimo é 0, enquanto o máximo é 22 + 21 + 20 + 2−1 + 2−2 + 2−3 + 2−4 + 2−5 = 4 + 2 + 1 + 0.5 + 0.25 + 0.125 + 0.0625 + 0.03125 = 7.96875. Como consideramos somente números positivos, a faixa vai de 0 a 7.96875. O número de valores diferentes que podemos armazenar é determinado pelo número de valores possíveis (2) elevados à potência do número de bits (8), ou 28 = 256.

11. Vimos como converter a representação interna de 4187000 (hexadecimal) no padrão de ponto flutuante IEEE 754 de volta ao valor original 16.875. Que valor 41118034 (hexadecimal) representa?

371

Weeks Apendice E.indd 371

20/06/12 10:38

372

APÊNDICE E

Resposta: >> disp(sprintf(‘%tx’,9.0938)) 41118034 Que valor 411570a4 representa no padrão de ponto flutuante IEEE 754? Resposta: >> disp(sprintf(‘%tx’,9.34)) 411570a4

13. Converta o ponto cartesiano (2, 5) em coordenadas polares. Resposta:

Como esse ponto encontra-se no primeiro quadrante, nenhuma correção é necessária.

Também podemos encontrar a resposta com o MATLAB: >> abs(2+5j) ans = 5.3852 >> angle(2+5j) ans = 1.1903

15. Explique o que significa quantificação (ou quantização) no tempo e quantificação em amplitude. Resposta: Quando um sinal contínuo (analógico) é amostrado, ou convertido em um sinal discreto no domínio do tempo, temos uma precisão limitada tanto em tempo quanto em amplitude. Ele é quantificado no tempo, o que significa que teremos amostras correspondentes às medições em diversos instantes, mas não teremos amostras entre elas. Assim, teremos amostras do sinal em x[n], ou x[0], x[1], x[2] etc. Mas NÃO teremos dados em x[1.5]. O sinal é quantificado em amplitude, o que significa que impomos nossa precisão sobre as amostras que coletamos. Cada amostra estará sujeita à faixa limitada que permitirmos. Qualquer número muito grande ou que possua uma parte muito pequena não será armazenado corretamente.

17. Por que existe um problema na utilização de ␪ = arctan(b/a) para a conversão em coordenadas polares? Resposta: O argumento para a função arctan será positivo ou negativo. Se tanto a quanto b forem negativos, os sinais negativos se cancelarão e o argumento será positivo. Portanto, a conversão fornece o mesmo resultado obtido quando tanto a quanto b são positivos, embora o ângulo esteja claramente em um quadrante diferente quando o ponto é traçado. Da mesma forma, a positivo e b negativo produzem o mesmo resultado que a negativo e b positivo. Assim, precisamos corrigir o ângulo com base no quadrante. (Para mais detalhes, consulte o Capítulo 1.)

Weeks Apendice E.indd 372

20/06/12 10:38

Respostas para Exercícios Selecionados

373

Capítulo 2

Todas as perguntas devem ser respondidas com o MATLAB, salvo menção em contrário. Além disso, sua resposta não deve ser uma simples chamada para outra função (embutida). Embora seja interessante conhecer tais funções, a ideia aqui é que você ganhe experiência resolvendo problemas por conta própria.

1. Escreva uma função MATLAB que ordene uma lista de números em ordem decrescente (do maior para o menor). Resposta % % % % % % % % %

mysort.m Funcao para ordenar lista de numeros em ordem decrescente (do mais alto para o mais baixo) exemplo: x = round(100*(0.5 - rand(1,100))); y = mysort(x); plot(y)

function sorted = mysort(in) % % % % %

Sim, poderiamos utilizar a funcao “sort” embutida. Tambem poderiamos utilizar as funcoes “min” e “max”. Mas, em vez disso, utilizaremos apenas comparacoes e loops. Isto se baseia em um algoritmo de ordenacao por flutuacao (“bolha”). Existem modos mais eficientes, mas este e simples.

% copie nossos dados sorted = in; % certifique-se de que executamos o loop ao menos uma vez swap = true; while (swap) swap = false; % padrao % Se examinarmos a lista inteira, % e nao permutarmos valores, % saberemos que a operacao esta concluida. for k=2:length(sorted) % Compare o valor atual com o seu vizinho if (sorted(k-1) < sorted(k)) % permute valores temp = sorted(k-1); sorted(k-1) = sorted(k); sorted(k) = temp; swap = true; end end end

3. Se x = [121, 145, 167, 192, 206], o que é y = 3x − 4? Seria sum(y) o mesmo valor que 3sum(x) − 4? Resposta: Não, sum(3x − 4) não possui o mesmo valor que 3sum(x) − 4. No primeiro cálculo, tomamos cada valor de x e o multiplicamos por 3, depois subtraímos 4 antes de encontrarmos a soma. No segundo cálculo, encontramos a soma primeiro e só depois efetuamos a multiplicação e a subtração. Como resultado, −4 é efetivamente aplicado uma vez para cada valor de x no primeiro cálculo, enquanto no segundo ele é aplicado somente uma vez. >> x = [121, 145, 167, 192, 206]; >> y = 3*x - 4 y = 359

431

497

572

614

>> sum(y)

Weeks Apendice E.indd 373

20/06/12 10:38

374

APÊNDICE E

ans = 2473 >> 3*sum(x) -4 ans = 2489

5. Utilize o MATLAB para traçar a senoide 2cos(2␲1500t + ␲/2), começando em t = 0 segundo. Certifique-se de utilizar pontos suficientes para gerar um gráfico suave e mostre apenas algumas repetições. Resposta: >> t = 0:0.00001:0.002; >> x = 2 * cos(2*pi*1500*t + pi /2); >> plot(t, x, ‘k’)

7. Números de ponto flutuante (reais) são armazenados em binário com precisão finita em computadores. (a) Utilize o MATLAB para descobrir o menor número que podemos armazenar. Em outras palavras, podemos armazenar 1/(2n) em uma variável quando n é pequeno, mas quão grande n pode ser antes que a variável seja considerada zero? (Dica: utilize a precisão padrão.) Resposta: O código MATLAB a seguir encontrará o menor número que podemos representar. n=0; my_value = 1/(2^n); while (my_value > 0) n = n + 1; my_value = 1/(2^n); end n = n - 1; sprintf(‘n = %d, 1/(2^n) = %f’, n, 1/(2^n) ) (Estas respostas foram obtidas utilizando-se um notebook, não nosso servidor.) n = 1023. (b) Qual o maior número que podemos armazenar? Em outras palavras, podemos armazenar 2n em uma variável, mas quão grande n pode ser? O que acontece quando n é muito grande? Resposta: n=0; my_value = 2^n; while (my_value < Inf) n = n + 1; my_value = 2^n; end n = n - 1; sprintf(‘n = %d, 2^n = %f’, n, 2^n ) n = 1023. Se for muito grande, o valor será armazenado como Inf, ou infinito. O fato de a resposta aqui ser a mesma que a da Parte a não chega a surpreender. Em ambos os problemas, estamos armazenando o número +1 × 2n, onde n é positivo (como na Parte b) ou negativo (como na Parte a). Ou seja, o expoente muda à medida que n torna-se maior ou menor. A diferença entre as Partes a e b é o sinal do expoente. (c) Em relação ao número 1 + 1/(2n), quão grande n pode ser? Sua resposta é a mesma que a da Parte a? Por que ou por que não? Resposta: n=0; my_value = 1+1/(2^n); while (my_value ~= 1) n = n + 1;

Weeks Apendice E.indd 374

20/06/12 10:38

Respostas para Exercícios Selecionados

375

my_value = 1+1/(2^n); end n = n - 1; sprintf('n = %d, 1+1/(2^n) = %f', n, 1+1/(2^n) ) Aqui, n = 52. À medida que n torna-se maior, o número 1 + 1/(2n) aproxima-se mais e mais de 1. Quando ele é armazenado como 1, sabemos que alcançamos nosso limite. Esta resposta difere daquela na Parte a, pois o número a ser armazenado é 1.0[0..0]1, o que significa que o expoente permanece o mesmo. Na Parte a, a mantissa pode permanecer a mesma à medida que o expoente torna-se menor.

9. Escreva um programa que mostre o complexo conjugado da entrada do usuário (utilizando o comando input). Se, por exemplo, o usuário inserir −5.1 + 4.3j, o programa deverá imprimir −5.1 − 4.3j. Resposta: c = input('Por favor insira um numero complexo: '); str = 'O complexo conjugado e'; str = strcat(str, ... sprintf(' %5.2f%+5.2fj', real(c), -imag(c))); disp(str);

11. Suponha que você deseje comparar duas listas, x e y. Elas podem ter extensões diferentes, mas deverão ter a mesma extensão para a comparação (tal como sum(x-y)). Escreva uma função que estenda a mais curta das duas listas com zeros de modo que ambas passem a ter a mesma extensão. (Dica: embora seja possível fazer isso com um loop, existe uma solução mais concisa.) Resposta: function [newx, newy] = arraySameLen(x, y) % Descubra qual a mais longa if (length(x) < length(y)) % y e mais longa, portanto ajuste x lengthDiff = length(y) - length(x); newy = y; newx = [x, zeros(1, lengthDiff)]; elseif (length(y) < length(x)) % x e mais longa, portanto ajuste y lengthDiff = length(x) - length(y); newx = x; newy = [y, zeros(1, lengthDiff)]; else % elas devem ter a mesma extensao newx = x; newy = y; end

13. Escreva um programa que converta um número duplo em sua representação binária. (Dica: se você exibir o número tanto em hexadecimal quanto em binário, será fácil conferi-lo.) Resposta: Chame nosso número de some_double. Examine o programa double2bin.m no site da LTC Editora. Primeiramente, anotaremos o sinal e, em seguida, passaremos a tratar o número como positivo. if (some_double < 0) % lembre-se de que ele e negativo sign_bit = '-'; else % lembre-se de que ele e positivo sign_bit = '+'; end x = floor(abs(some_double)); Utilizamos a função floor para separar a parte inteira da parte fracionária. Em seguida, decodificamos a parte inteira, armazenando os zeros e uns na lista str.

Weeks Apendice E.indd 375

20/06/12 10:38

376

APÊNDICE E

s = 0; str = ''; % algoritmo para decimal inteiro em binario while (x > 0) if ((x/2) == fix(x/2)) % par s = s + 1; str(s) = '0'; else % impar s = s + 1; str(s) = '1'; end x = fix(x/2); end A seguir, os bits devem ser invertidos, pois normalmente seriam reescritos do último para o primeiro. str2 = ['0', str(s:-1:1)]; clear str Agora isolaremos a parte fracionária e a decodificaremos. Note que utilizamos a multiplicação por dois, em vez da divisão por dois, para deslocar os bits. x = abs(some_double) - floor(abs(some_double)); s = 0; while (x > 0) if ((x*2) >= 1) % um s = s + 1; str(s) = '1'; else % zero s = s + 1; str(s) = '0'; end x = x*2 - fix(x*2); end Só o que é preciso fazer agora é imprimir o número. Também empregaremos a função sprintf para verificar como ele fica em hexadecimal. disp(sprintf('%bx', some_double)) bin_number = [sign_bit, str2, '.', str] Eis um exemplo de execução: >> double2bin(16.875) 4030e00000000000 ans = +010000.111 A conversão do número hexadecimal 4030e em binário com o quadro de consulta resulta em: 0100 0000 0011 0000 1110 O primeiro bit (0) significa positivo; os 11 bits seguintes correspondem ao expoente deslocado1 100 0000 0011, que é igual a 1027. O deslocamento2 é igual a 1023, de modo que o expoente real é 1027 − 1023 = 4. Por fim, temos .0000111 para o número. Acrescente o bit oculto para obter 1.0000111. Para concluir, desloque o ponto fracionário por 4 bits, de acordo com o expoente, para obter 10000.111. Isso corresponde à nossa conversão. 1 2

Biased exponent, também conhecido como “característica”. (N.T.) Bias, também conhecido como “desvio”. (N.T.)

Weeks Apendice E.indd 376

20/06/12 10:38

Respostas para Exercícios Selecionados

377

Capítulo 3 1, FIR é um acrônimo de que expressão? O que ela significa? Resposta: Ela representa um filtro de resposta finita ao impulso (finite impulse response). Ela significa que, na presença de uma entrada diferente de zero e depois seguida de zeros, ao final obteremos zeros na saída.

3. O que significa “linear”? Resposta: Linearidade é uma propriedade de alguns sistemas (como um filtro FIR ou IIR) na qual a relação de saída contém uma soma de entradas multiplicadas por constantes.

5. Escreva um código MATLAB para computar a saída (y[n]) para convolução, dados coeficientes de filtro FIR (b[k]) e entrada (x[n]) quaisquer. Resposta: Uma resposta fácil (e preguiçosa) seria a utilização da função embutida conv do MATLAB, da seguinte maneira: >> b = [ 0.1, 0.2, 0.3, 0.2, 0.1]; >> x = 1:100; >> y = conv(x, b); Uma melhor solução seria a utilização de loops para imitar as operações executadas na convolução, como na função a seguir. % % myconv2.m Uma solucao para o problema, % de como implementar a equacao % de diferenca em MATLAB. % Isso deve ser equivalente a funcao conv do MATLAB. % Esta funcao NAO constitui o modo mais eficiente de implementacao, % mas trata-se do mais direto. % Uso: % Y = myconv2(A, X); % function [Y]= myconv2(A, X) % N % M

numero de entradas – 1 (pois contamos de 0..N) = length(X)-1; numero de coeficientes de filtro – 1 = length(A)-1;

% numero de saidas = N + M % uma vez que normalmente contariamos de 0...M+N, % em MATLAB temos de somar 1 a este resultado % quando utilizado como um indice de lista, por exemplo, conte de 1...M+N+1 for n=0:M+N, % inicialize nossa saida em zero Y(n+1)=0; % execute o somatorio for k=0:M, x_index = n-k; % X(n) = 0 sempre que n < 0 ou n < extensao(X) if (and(x_index >= 0, x_index > y2 = myconv2(x, b); >> sum(abs(y2-y)) ans = 1.8385e-13

Weeks Apendice E.indd 377

20/06/12 10:38

378

APÊNDICE E

7. Que palavra descreve a operação a seguir? ________ de x[n] e b[n] produz y[n].

Resposta: Convolução

9. O que chamamos de h[n]? Por que ela é importante? Resposta: Chamamos de h[n] a resposta ao impulso. Ela mostra o comportamento de um filtro diante de uma única entrada diferente de zero. Para filtros FIR, ela mostra que efeito o filtro exerce nas frequências de um sinal de entrada.

11. Em relação ao sistema1 e o sistema2 a seguir, trata-se de sistemas lineares? São eles invariantes no tempo? Eles são causais? Explique. Considere x[n] a entrada e y[n] a saída onde sistema1 é y1[n] = x[n] − x[n − 1] e sistema2 é y2[n] = 3x[n] + 1. Resposta: Para o sistema 1: Considere v1[n] a saída para uma entrada x1[n], v2[n] a saída para x2[n] e v3[n] a saída para uma entrada combinada x3[n] = x1[n] + x2[n].

Por passar também pelo teste de escalonamento, esse sistema é linear. Ele não utiliza quaisquer valores futuros da entrada, pois o índice é sempre igual a n ou n – (algum valor); portanto esse sistema é causal. Um atraso k na entrada produz um atraso k igual na saída; portanto este sistema é invariante no tempo. Para o sistema 2: Considere v1[n] a saída para uma entrada x1[n], v2[n] a saída para x2[n] e v3[n] a saída para uma entrada combinada x3[n] = x1[n] + x2[n].

portanto esse sistema não é linear. Ele não utiliza quaisquer valores futuros da entrada, pois o índice é sempre igual a n ou n – (algum valor); portanto esse sistema é causal. Um atraso k na entrada produz um atraso k igual na saída; portanto esse sistema é invariante no tempo.

13. Suponha que tenhamos um sistema em que y[n] = 2x[n] + x[n − 1]. (a) Esse sistema é causal? Resposta: Ao examinarmos os índices das entradas em relação à saída, constatamos que o índice da saída é tão grande (ou maior) quanto todos os índices utilizados para as entradas. Assim, concluímos que esse sistema é causal. (b) Esse sistema é linear? Resposta: Quanto à linearidade, temos de verificar duas coisas. Primeiramente, ele deve ser invariante em relação ao escalonamento de entrada. Considere y1[n] = 2x[n] + x[n − 1]. Nós a multiplicaremos por uma constante c, para encontrar:

Em seguida, multiplicaremos todos os valores de x por c, para obtermos:

Weeks Apendice E.indd 378

20/06/12 10:38

Respostas para Exercícios Selecionados

379

Agora submetemos essa entrada ao sistema, para encontrarmos:

Verificamos que y1[n] = y2[n]; portanto sabemos que ele é invariante em relação ao escalonamento de entrada. Agora considere v1[n] a saída do sistema para uma entrada x1[n]. Além disso, considere v2[n] a saída do sistema para uma entrada x2[n]. Definimos x3[n] = x1[n] + x2[n], e v3[n] como a saída para x3. Se o sistema for linear, v3[n] será igual a v1[n] + v2[n].

Uma vez que v3[n] = v1[n] + v2[n], concluímos que esse sistema é linear. (c) Esse sistema é invariante no tempo? Resposta: Considere y1[n] = 2x[n] + x[n − 1]. Deslocaremos y1[n] por k unidades para obter y1[n − k] = 2x[n − k] + x[n − k − 1]. Em seguida, atrasaremos x[n] por k unidades e o rotularemos como x2[n].

Existem k 0s em x2. Agora considere y2[n] = 2x2[n] + x2[n − 1] = 2x[n − k] + x[n − k − 1]. Vemos que y1[n − k] = y2[n], ou seja, que a saída de uma entrada atrasada é a mesma que a saída atrasada da entrada. Deste modo, concluímos que o sistema é invariante no tempo.

15. Escreva uma função MATLAB que calcule a saída de um filtro IIR. Considere que o filtro possui quatro coeficientes de alimentação direta e três coeficientes de realimentação, definidos no topo de sua função. Resposta: function y = simulateIIR(x) % Coeficientes de alimentacao direta de exemplo feedforward = [ 1, 5, 3, 4 ]; % Coeficientes de realimentacao de exemplo feedback = [ 0.2, 0.6, 0.1 ]; % A forma direta: % y[n] = 1 x[n] + 5 x[n-1] + 3 x[n-2] + 4 x[n-3] + % 0.2 y[n-1] + 0.6 y[n-2] + 0.1 y[n-3] y(1) = 0; % valor inicial de modo que xof(y,1) seja valido. % Implemente a equacao diretamente for n=1:length(x+length(feedforward)-1) % A ideia e que xof(x,n-2) seja o mesmo que x[n-2], % somente com verificacao de erro no indice. % Alem disso, xof(y,n-2) e o mesmo que y[n-2]. y(n) = feedforward(1)*xof(x,n) + feedforward(2)*xof(x,n-1) ... + feedforward(3)*xof(x,n-2) + feedforward(4)*xof(x,n-3) ... + feedback(1)*xof(y,n-1) + feedback(2)*xof(y,n-2) + ... feedback(3)*xof(y,n-3); end % Esta funcao implementa x[n]. Mas ela verifica quanto % a valores de indice (n) validos e retorna 0 se x[n] nao estiver

Weeks Apendice E.indd 379

20/06/12 10:38

380

APÊNDICE E

% definida. function val = xof(x,n) if ((n < 1) || (n > length(x))) val = 0; else val = x(n); end

Capítulo 4 1. Quais os valores mínimo e máximo para x(t)?

Resposta: A função cos possui um máximo de +1 e um mínimo de −1. Assim, os valores mínimo e máximo para x(t) são 3 e –3, respectivamente.

3. O sinal analógico, x(t), é definido como:

(a) Trace cada componente de frequência (no domínio do tempo) separadamente. Resposta: >> >> >> >> >> >> >> >> >> >>

t = 0:0.000001:0.001; x1 = 3*cos(2*pi*2000*t + pi/4); x2 = 2*cos(2*pi*5000*t); x3 = cos(2*pi*11000*t - pi/7); subplot(3,1,1); plot(t, x1); title('3cos(2\pi 2000 t + \pi/4)') subplot(3,1,2); plot(t, x2); title('2cos(2\pi 5000 t)') subplot(3,1,3); plot(t, x3); title('cos(2\pi 11000 t - \pi/7)')

(b) Represente graficamente essa função (no domínio do tempo). Resposta: >> t = 0:0.000001:0.001; >> x = 3*cos(2*pi*2000*t + pi/4) + 2*cos(2*pi*5000*t) ... + cos(2*pi*11000*t - pi/7); >> plot(t, x) (c) Represente x(t) em termos de uma frequência fundamental, amplitudes e fases. Resposta: O maior valor comum entre 2000, 5000 e 11000 é 1000. Portanto, f0 = 1000 Hz. Deste modo, podemos representar x como:

Para as amplitudes, sabemos que a2 = 3, a5 = 2 e a11 = 1. Todas as demais amplitudes são iguais a 0. Para os ângulos de fase, vemos que ␾2 = ␲/4, ␾5 = 0 e ␾11 = −␲/7. Todos os demais ângulos de fase são iguais a 0.

5. Qual a diferença entre amplitude e magnitude? O que significa |x(t)|? Resposta: Amplitude corresponde a uma quantidade sem unidades – pode ser positiva ou negativa. Magnitude corresponde a uma quantidade sem unidades, sempre positiva. Quando temos uma função entre barras verticais, ela destinase ao cálculo da magnitude. Ela é implementada em MATLAB como a função abs; o valor absoluto.

Weeks Apendice E.indd 380

20/06/12 10:38

Respostas para Exercícios Selecionados

381

7. Qual o outro nome para um traçado da informação de frequência de um sinal? Resposta: Espectro

9. Por que as amplitudes são sempre positivas? Resposta: Tecnicamente, amplitudes podem ser positivas ou negativas. Em termos práticos, uma amplitude negativa não altera o sinal, exceto para deslocá-lo no tempo. Podemos obter o mesmo efeito alterando o ângulo de fase. Assim, não precisamos de amplitudes negativas.

11. Crie seu próprio sinal harmônico (utilizando MATLAB) com no mínimo 10 senoides. Qual é a equação para ele? Trace o gráfico para ele, tal como na Figura 4.20. Resposta: f0 = 7; % frequencia fundamental em Hz % amplitudes a = [0.7, 0.5, 0.1, 0.8, 0.9, 0.5, 0.2, 0.1, 0.4, 0.6]; phi = [pi/4, 0, pi/6, -pi/3, pi/5, pi/10, pi/8, -pi/8, -pi/4, 0]; % tempo simulado t = 0:0.001:0.28; % x inicial x = a(1)*cos(2*pi*f0*t + phi(1)); % agora adicione as 9 outras senoides for k=2:10 x = x + a(k)*cos(2*pi*k*f0*t + phi(k)); end plot(t, x, 'b'); title('Sinal harmonico com 10 senoides'); xlabel('Tempo (seg)'); ylabel('Amplitude'); axis tight;

13. Se z = (2, 3), o que é z em termos de notação ej? Resposta: r = sqrt(4 + 9) = sqrt(13) = 3.6055 ␪ = arctan(3/2) = 0.9828 radianos (56.3 graus) Portanto, z é igual a 3.6055ej0.9828.

15. É possível reduzir 6ej2␲ para um número real? Resposta:

Como a parte complexa é igual a zero, esse fasor reduz-se a um número real.

17. O que é 2ej3␲/8ej2␲100t em termos de seno e cosseno? Resposta: Primeiramente, combine os expoentes.

Considere ␪ = 3␲/8 + 2␲100t.

Weeks Apendice E.indd 381

20/06/12 10:38

382

APÊNDICE E

19. Considere o fasor girante ej␻t+␾. O que ␻ nos informa sobre sua aparência? O que ␾ nos informa sobre sua aparência? Resposta: ␻ nos informa sobre a velocidade de rotação e sobre a direção. Se ␻ for positivo, ele girará no sentido antihorário. Se ele for negativo, girará no sentido horário. Sim, podemos ter uma frequência positiva ou negativa.

␾ nos fornece a posição inicial, ou seja, na qual o fasor se encontra no instante de tempo 0.

21. Qual o complexo conjugado de ej␾? Resposta: e–j␾

23. Trace 4 + j5 em um gráfico e depois multiplique-o por j, traçando o resultado em seguida. Multiplique esse resultado por j novamente e indique-o no gráfico. Repita esta operação uma última vez. O que você observa? Que ângulos os quatro fasores possuem? Resposta: Observamos que o fasor é girado em 90° no sentido anti-horário sempre que o multiplicamos por j. Os quatro fasores são:

Os ângulos (em graus) são: >> v = [4 + 5j, -5 + 4j, -4 - 5j, 5 - 4j]; >> disp(angle(v)*360/(2*pi)) 51.3402 141.3402 -128.6598 -38.6598 Se adicionarmos 360° aos dois últimos ângulos, obteremos 231.3402 e 321.3402. Claramente, cada ângulo corresponde a 90° mais o ângulo anterior.

25. Considere z1 = e−j␲/5 e z2 = 3ej␲/6. O que é z3 = z1 × z2? Desenhe todos os três fasores em um gráfico. Resposta: Podemos fazer isso em MATLAB para conferir o traçado feito à mão. Posicionamos o ponto (0,0) entre os valores zn de modo que haja uma linha indo para a origem. >> >> >> >> >> >>

z_1 = exp(-j*pi/5); z_2 = 2*exp(j*pi/6); z_3 = z_1*z_2; x = real([z_1, 0, z_2, 0, z_3]); y = imag([z_1, 0, z_2, 0, z_3]); plot(x,y,'b')

27. Vimos que podemos somar fasores girantes quando eles giram na mesma frequência. O que acontecerá se os somarmos quando suas frequências não coincidirem? Utilize o MATLAB para encontrar (ponto por ponto) e traçar z3 = z1 + z2, em que z1 = 4ej(2␲25t − 5␲/4) e z2 = 5ej(2␲33t + pi/6). Considere que t varia de 0 a 1 com um pequeno incremento apropriado. Resposta: Podemos traçar esses fasores girantes de duas maneiras diferentes. Aqui, traçamos os valores reais sobre o eixo x e os imaginários sobre o eixo y. t = z_1 z_2 z_3

Weeks Apendice E.indd 382

0:0.01:1; = 4*exp(j*(2*pi*25*t - 5*pi/4)); = 5*exp(j*(2*pi*33*t + pi/6)); = z_1 + z_2;

20/06/12 10:38

Respostas para Exercícios Selecionados

383

% insira 0 entre cada valor z0_1(1:length(z_1)*2) = 0; z0_2(1:length(z_2)*2) = 0; z0_3(1:length(z_3)*2) = 0; for k = 1:length(z_1) z0_1(k*2 -1) = z_1(k); z0_2(k*2 -1) = z_2(k); z0_3(k*2 -1) = z_3(k); end % efetue os tracados figure(1); plot(real(z0_1), imag(z0_1), 'b'); figure(2); plot(real(z0_2), imag(z0_2), 'b'); figure(3); plot(real(z0_3), imag(z0_3), 'b'); % Agora trace-os como frequencias figure(4); plot(1:length(z_1), abs(fft(z_1)), 'r', ... 1:length(z_2), abs(fft(z_2)), 'g', ... 1:length(z_3), abs(fft(z_3)), 'b'); Vemos a partir dos traçados que a soma dos dois produz um novo sinal que contém ambas as frequências, exatamente como a soma de duas senoides (mas sem a imagem espelhada no traçado de frequência).

Capítulo 5 1. Se o período de amostragem for de 8 ms, qual será a frequência de amostragem? Resposta: 1/0.008 = 125 Hz

3. O que é subamostragem? Por que você a utilizaria (ou por que não)? Resposta: A subamostragem ocorre quando não coletamos amostras com frequência suficiente. Ela é indesejável, pois não seremos capazes de recriar fidedignamente o sinal amostrado. Uma exceção para isso está em pesquisas recentes nas quais a subamostragem é empregada em aplicações de compressão.

5. O que significa “reconstrução”? Resposta: A conversão de um sinal digital em um analógico, assim chamado porque o sinal era tipicamente analógico, para começar.

7. Se você conectasse a saída de um ADC à entrada de um DAC, a saída do DAC seria exatamente a mesma que a entrada do ADC? Por que ou por que não? Resposta: Idealmente, sim. A saída seria exatamente igual à entrada. Mas existem aspectos práticos a considerar que tornariam os dois sinais diferentes. O circuito que compõe o ADC e o DAC pode não ser suficientemente rápido para lidar com todos os valores de sinal analógico possíveis.

9. Mostre que a sobreposição espectral ocorre para mais de uma senoide, tal como quando amostramos x(t) = 3 cos(2␲300t + ␲/3) + 8 cos(2␲800t − ␲/5) a fs = 500 Hz. Resposta: A frequência de 800 Hz é claramente maior que fs (500 amostras/segundo), portanto ela terá uma sobreposição espectral em (800 − fs) = 300 Hz. Note que o componente de frequência de 300 Hz terá uma réplica em −300 Hz, que por sua vez possui uma réplica em fs + (−300) = 200 Hz.

11. Suponha que tenhamos um sinal com frequências entre 18 kHz e 24 kHz. Se amostrássemos o sinal de tal modo que houvesse duas réplicas do mesmo (entre −18 kHz e +18 kHz), qual(is) seria(m) nossa(s) frequência(s) de amostragem?

Weeks Apendice E.indd 383

20/06/12 10:38

384

APÊNDICE E

Resposta: B (largura de banda) está entre 18 e 24 kHz. B = 24 − 18 = 6 kHz. fc (frequência central) = (18 + 24)/2 = 21 kHz m = número de réplicas = 2

2B = 12 kHz (Nyquist); portanto fs na faixa de 16.67 a 18 kHz é adequada.

13. Suponha que tenhamos um sinal com frequências entre 10 MHz e 15 MHz. Se amostrássemos o sinal, que frequência(s) de amostragem possível(is) poderíamos utilizar? Note quantas réplicas do sinal haveria (ou seja, entre − 10 MHz e +10 MHz). Resposta: B (largura de banda) está entre 10 e 15 MHz. B = 15 − 10 = 5 MHz. Fc (frequência central) = (10 + 15)/2 = 12.5 MHz m = número de réplicas

2B = 10 MHz (Nyquist); portanto fs na faixa de 15 a 20 MHz é adequada. Além disso, fs = 10 MHz é adequada. Qualquer coisa além de m = 2 não funcionará.

15. Considere que sinal

seja amostrado a 10 kHz. (a) O que a equação x[n] se torna? Resposta:

Sabemos que Ts = 1/fs = 1/10000 Hz.

(b) Trace x[n]. Resposta: Isso pode ser feito em MATLAB. >> n = 1:100; >> x = 3*cos(2*pi*n*0.2 + pi/4) + 2*cos(2*pi*n*0.5) ... + cos(2*pi*n*1.1 - pi/7); >> plot(n, x) (c) Se x(t) for amostrado a 10 kHz, quais sobreposições espectrais o sinal de 5 kHz terá? Resposta: A frequência de 5 kHz possui sobreposições espectrais a cada ±10 kHz, ou seja, em −20 kHz, −10 kHz, −5 kHz, 15 kHz, 25 kHz etc. Cada uma delas também aparece espelhada em torno de 0 Hz, ou seja, +20 kHz, +10 kHz, +5 kHz, −15 kHz, −25 kHz etc.

Weeks Apendice E.indd 384

20/06/12 10:38

Respostas para Exercícios Selecionados

385

(d) Qual é a frequência de Nyquist crítica? Resposta: Isso corresponde a duas vezes a largura de banda. Largura de banda = 11000 − 2000 = 9000 Hz, de modo que a frequência de Nyquist crítica é de 18,000 Hz. A taxa de amostragem deve ser de no mínimo 18 kHz. (e) Você poderia utilizar uma amostragem de banda limitada com esse sinal? Por que (ou por que não)? Resposta: A amostragem de banda limitada utiliza uma frequência de amostragem de no mínimo 2B e situa uma réplica entre o conteúdo de frequência original e 0 Hz. O problema aqui é que não é possível alocar a réplica. Não existe uma forma de uma banda de 9 kHz caber no intervalo de 0 a 2 kHz. Uma vez que haverá sobreposição entre o conteúdo de frequência original e a réplica, não devemos utilizar a amostragem de banda limitada neste caso.

17. Suponha que tenhamos os seguintes sinais:

(a) Trace cada sinal. Na mesma página, mostre também o traçado do sinal com o argumento da frequência negativado (ou seja, cos(−2␲ft − ␾)). Resposta: >> >> >> >> >> >> >> >> >> >> >> >> >>

t = 0:0.00001:0.001; x1 = 6*cos(2*pi*7000*t + pi/2); x2 = 4*cos(2*pi*8000*t); x3 = 2*cos(2*pi*6000*t); x1_n = 6*cos(-2*pi*7000*t - pi/2); x2_n = 4*cos(-2*pi*8000*t); x3_n = 2*cos(-2*pi*6000*t); subplot(3,1,1); plot(t, x1, 'b', t, x1_n, 'r') subplot(3,1,2); plot(t, x2, 'b', t, x2_n, 'r') subplot(3,1,3); plot(t, x3, 'b', t, x3_n, 'r')

(b) O que você pode concluir acerca desses traçados? Resposta: Os sinais x1 e x1_n são o mesmo, assim como o são x2 e x2_n e x3 e x3_n. Assim, a função cosseno retorna o mesmo valor, esteja ou não o argumento negativado. (c) Sua conclusão seria a mesma se fosse utilizada a função seno? Por que ou por que não? (Não é preciso mostrar um gráfico, mas explique sua resposta.) Resposta: Não, a função seno não retorna o mesmo valor para um argumento negativado. Na prática, ela retorna um valor negativado para um argumento negativado, ou seja, sin(−␪) = −sin(␪). Tal fato advém das definições de seno e cosseno, ou seja, em um gráfico, x é positivo caso o ângulo esteja entre 0 e ␲/2 radianos ou entre −␲/2 e 0 radianos.

19. Dados x[n] = x(nTs) = cos(2␲1000nTs)/2 + cos(2␲2000nTs + ␲/4) e fs = 8 kHz, utilize o MATLAB para encontrar as primeiras 10 amostras x[n] (n = 0, …, 9). Resposta: Xsize = 10; Ts = 1/8000; % Gere nosso sinal X n=0:Xsize-1; x(n+1) = cos(2*pi*1000*n*Ts)/2 + cos(2*pi*2000*n*Ts + pi/4); sprintf('As primeiras %d amostras sao : ',Xsize) x

Weeks Apendice E.indd 385

20/06/12 10:38

386

APÊNDICE E

21. Suponha que você possua um sinal com frequências entre 1 kHz e 22 kHz e que haja ruído entre 100 kHz e 102 kHz. (a) Se você amostrar à taxa de 44,000 amostras/segundo, o ruído interferirá no sinal? Resposta: Sim, interferirá. O ruído de 100 kHz terá uma réplica em 100 − 44 − 44 = 22 kHz. O ruído de 102 kHz terá uma réplica em 102 − 44 − 44 − 44 = −30 kHz, que também aparecerá em −30 + 44 = 14 kHz. (b) Se você amostrar o sinal à taxa de 30,000 amostras/segundo, haverá um problema. Qual? Resposta: A taxa de amostragem deve ser de, no mínimo, duas vezes a largura de banda. Tal taxa de amostragem viola o critério de Nyquist.

23. Se o período de amostragem é de 25 milissegundos, qual é a frequência de amostragem? E se o período de amostragem for de 25 microssegundos? Resposta: A frequência de amostragem fs é igual a 1/Ts, onde Ts é o período de amostragem. Ts = 0.025 segundo, de modo que fs = 40 amostras por segundo. Se Ts = 0.000025 segundo, então fs = 40000 amostras por segundo.

Capítulo 6 1. Suponha que precisássemos amostrar um sinal à taxa de fs = 5000 amostras por segundo e que tivéssemos de coletar 250 amostras. Após executar a DFT, encontramos os primeiros 10 resultados abaixo. O que podemos concluir sobre as frequências presentes em nosso sinal de entrada? (Considere que os demais valores para X[m] até m = 125 são iguais a 0.)

Resposta: Temos um componente DC (0 Hz) de 10 unidades, e nas frequências de análise: f(3) = 3*5000/250 = 60 Hz f(5) = 5*5000/250 = 100 Hz f(9) = 9*5000/250 = 180 Hz Temos senoides de magnitudes 4.47, 1 e 4.47, respectivamente (não há unidades para esses números). Essas senoides possuem fases de 1.107 radiano (63.4 graus), 0 e −1.107 radiano (−63.4 graus), respectivamente. (As fases podem ser fornecidas em graus em vez de radianos, mas a resposta deve OBRIGATORIAMENTE especificar graus ou radianos.)

3. A transformada de Fourier foi aplicada em uma sequência de entrada, x[n]. o resultado obtido foi: X[m] = {3, 2 + j4, 1, 5 − j3, 0, 0, 0, 5 + j3, 1, 2 − j4}. Dado que x[n] foi amostrado a uma taxa fs = 100 amostras/segundo, (a) Qual o componente DC para esse sinal? Resposta: Há um total de 10 amostras. Temos um componente DC (0 Hz) de 3 unidades. (b) Que frequências estão presentes na entrada? Ordene-as de acordo com a amplitude. Resposta: Nas frequências de análise: f(1) = 1*100/10 = 10 Hz Magnitude: sqrt1(20) unidades = 4.5 unidades f(2) = 2*100/10 = 20 Hz Magnitude: 1 unidade f(3) = 3*100/10 = 30 Hz Magnitude: sqrt(34) unidades = 5.8 unidades f(4) = 4*100/10 = 40 Hz Magnitude: 0 unidade f(5) = 5*100/10 = 50 Hz Magnitude: 0 unidade (Vamos parar por aqui.) Os componentes de frequência são ordenados assim (do maior para o menor): 30 Hz, 10 Hz e 20 Hz. (c) Utilize MATLAB para encontrar x[n]. Resposta: >> X = [3, 2+4j, 1, 5-3j, 0, 0, 0, 5+3j, 1, 2-4j]; >> x = ifft(X); 1

Sqrt = square root = raiz quadrada. (N.T.)

Weeks Apendice E.indd 386

20/06/12 10:38

Respostas para Exercícios Selecionados

>> disp(x(1:5)) 1.9000 0.4768

-1.6607

-0.2899

0.4476

>> disp(x(6:10)) -0.9000 0.2468

1.9371

0.5663

0.2760

387

5. Experimente um filtro FIR de cinco taps utilizando os dados aleatórios a seguir. Note que esse comando sempre retornará valores diferentes. x = round(rand(1, 20)*100) Utilize h1[k] = {0.5, 0.5, 0.5, 0.5, 0.5} e compare-o com h2[k] = {0.1, 0.3, 0.5, 0.3, 0.1} e h3[k] = {0.9, 0.7, 0.5, 0.7, 0.9}. Represente graficamente a saída do filtro e sua resposta de magnitude em frequência. Certifique-se de utilizar os mesmos valores x. Crie um gráfico com as saídas dos filtros e outro gráfico com as respostas de frequência. Se julgarmos cada conjunto de coeficientes de filtro como um filtro passa baixa, qual será o melhor? Qual será o pior? Por quê? Resposta: x = round(rand(1, 20)*100); h1 = [0.5, 0.5, 0.5, 0.5, 0.5]; h2 = [0.1, 0.3, 0.5, 0.3, 0.1]; h3 = [0.9, 0.7, 0.5, 0.7, 0.9]; out1 = conv(x, h1); out2 = conv(x, h2); out3 = conv(x, h3); subplot(3,1,1); plot(out1); title('output 1'); subplot(3,1,2); plot(out2); title('output 2'); subplot(3,1,3); plot(out3); xlabel('filter outputs'); title('output 3'); figure(2); Out1 = fft(out1); Out2 = fft(out2); Out3 = fft(out3); subplot(3,1,1); plot(abs(Out1)); title('FMR of output 1'); subplot(3,1,2); plot(abs(Out2)); title('FMR of output 2'); subplot(3,1,3); plot(abs(Out3)); title('FMR of output 3'); O segundo filtro produz uma versão mais suave do original, embora ainda retenha as características do mesmo. Os coeficientes h2[k] possuem a transição mais suave (com 0.1 em cada extremidade). Os coeficientes de filtro em h3[k] possuem a transição mais abrupta (o primeiro e o último valor) e correspondem visivelmente ao pior filtro dentre os três.

7. O que é 3e−j2␲0.2 em coordenadas cartesianas complexas? (Isto é, na forma a + jb.) Dica: utilize a fórmula de Euler. Resposta:

Weeks Apendice E.indd 387

20/06/12 10:38

388

APÊNDICE E

9. Suponha que você tenha um sinal com conteúdo de frequência em 27.5 Hz e múltiplos inteiros do mesmo. Se a frequência de amostragem fosse de 8192 amostras/segundo, quantas amostras você leria para minimizar o vazamento espectral? Resposta:

Portanto teremos um ponto de frequência a cada 8192/N. Se um desses pontos corresponder exatamente a 27.5 Hz, teremos um nível mínimo de vazamento espectral. Suponha que utilizemos N = 200. As primeiras duas amostras terão frequências de análise em 0 Hz e em 40.96 Hz. Precisamos alterar N de modo que este número se aproxime de 27.5 Hz. Consideraremos m = 1 e examinaremos as frequências de análise para diferentes valores de N. Suponha que utilizemos N = 300. As duas primeiras amostras terão frequências de análise em 0 Hz e em 27.3067 Hz. Com base em mais algumas observações, podemos utilizar um loop para zeo de modo a convergirmos para a frequência desejada. for N = 295:302 fs/N end Não obtemos exatamente 27.5 Hz, mas chegamos perto com 27.4899 Hz quando N = 298. Assim, podemos utilizar algum múltiplo deste número, tal como 298, 2980 etc.

Capítulo 7 1. Qual é a transformada z de x = {3, 2, 4}? Resposta:

X(z) = 3z0 + 2z−1 + 4z−2 3. Um filtro FIR possui os seguintes coeficientes de filtro: {1, 1}. Qual é a resposta de frequência? Onde os zeros estão localizados? Utilize MATLAB para calcular a saída com uma entrada cos(2␲100t), amostrada a fs = 200 amostras/segundo. Resposta: Primeiramente, encontramos a resposta de frequência. h = [1, 1]; % simule a funcao impulso imp = zeros(1,1000); imp(10) = 1; % obtenha a resposta de frequencia H = fft(conv(h, imp)); half = 1:ceil(length(H)/2); plot(abs(H(half))) Vemos que se trata de um filtro passa-baixo. Agora encontraremos os zeros.

Vemos que ele possui um zero em z = −1. Por fim, simularemos um sinal amostrado para a entrada para esse filtro. n = 1:1000; x = cos(2*pi*100*n/200); % Agora passe pelo filtro y = conv(x, h); plot(y)

Weeks Apendice E.indd 388

20/06/12 10:38

Respostas para Exercícios Selecionados

389

Constatamos que y é zero, exceto para o início e o fim. A frequência de 100 Hz é muito alta (fs/2) quando a taxa de amostragem é de 200 amostras/segundo. Como as frequências baixas do sinal são filtradas, nossa expectativa é a de que ele seja atenuado.

5. Qual a transformada z da função de transferência, H(z), para os filtros a seguir? (a) Filtro FIR com coeficientes b[k] = {−17, 29, 107, 62} Resposta:

(b) Filtro IIR com coeficientes de alimentação direta b[k] = {−17, 29, 107, 62} e coeficientes de realimentação a[k] = {4, −7, −26, −15} Resposta:

7. Suponha que tenhamos um filtro com a função de transferência a seguir. Qual o valor de H(z) quando z =

2ej␲/6? E quando z = 2e−j2␲/6? Encontre os valores analiticamente e reduza a resposta a um único valor complexo. Em seguida, escreva um programa para demonstrá-lo.

Resposta:

>> z = 2*exp(j*pi/6); >> H = 1 - z^(-1) + z^(-2) H = 0.6920 + 0.0335i

Weeks Apendice E.indd 389

20/06/12 10:38

390

APÊNDICE E

Capítulo 8 1. Calcule (manualmente) como os sinais zd[n] e wd[n] seriam para o banco de filtros (mostrado no texto). Considere x = {8, 4, 0, 6, 3, 7, 2, 9}, a = 1/2 e b = 1/2. Certifique-se de mostrar seu trabalho. Resposta: Geração de z[n] por convolução com {b, −a}: 8 4 0 6 3 6 2 9 0.5 -0.5 -------------------------------------4 2 0 3 1.5 3 1 4.5 -4 -2 0 -3 -1.5 -3 -1 -4.5 ------------------------------------------4 -2 -2 3 -1.5 1.5 -2 3.5 -4.5 A subamostragem remove cada valor sim, outro não, para fornecer zd[n] = {4, −2, −1.5, −2, −4.5}. Vamos gerar w[n] por convolução com {a, b}: 8 4 0 6 3 6 2 9 0.5 0.5 -------------------------------------4 2 0 3 1.5 3 1 4.5 4 2 0 3 1.5 3 1 4.5 ------------------------------------------4 6 2 3 4.5 4.5 4 5.5 4.5 A subamostragem remove cada valor sim, outro não, para fornecer wd[n] = {4, 2, 4.5, 4, 4.5}.

3. O que é multirresolução (ou seja, uma transformada de wavelet com mais de uma oitava)? Demonstre esta ideia com uma figura. Resposta: Multirresolução é a repetição recursiva de uma transformada (posteriormente repetindo a transformada inversa de acordo). Para um exemplo, examine as figuras referentes à multirresolução no texto.

5. Escreva um programa MATLAB que execute a transformada (para uma oitava) e a transformada inversa a seguir. Examine a Figura 8.1, onde LPF = {1, 1}, HPF = {−1, 1}. (LPF significa filtro passa-baixo e HPF significa filtro passa-alto.) Para a transformada inversa, examine a Figura 8.2. Onde ILPF = {−1, −1}, IHPF = {−1 , 1} (ILPF significa filtro passa-baixo inverso e IHPF significa filtro passa-alto inverso.) Será y uma versão escalonada de x − por exemplo, y[n] × (−1/4) = x[n]? Resposta: Primeiramente, utilizaremos a função conv para executar a convolução. Em seguida, simularemos a subamostragem. O próximo passo será simular a sobreamostragem e executar a transformada inversa. Por fim, somaremos os resultados dos dois canais para encontrar y. Observe como nos certificamos de que x e y tenham a mesma extensão (uma vez que a filtragem torna a saída mais extensa). % encontre a transformada direta channel1 = conv(x, [1, 1]); channel2 = conv(x, [-1, 1]); % subamostre ch1_out = channel1(2:2:length(channel1)); ch2_out = channel2(2:2:length(channel2)); clear channel1 channel2 % agora desfaca a transformada % sobreamostre channel1(1:length(ch1_out)*2) = 0; channel2(1:length(ch2_out)*2) = 0; for k = 1:length(ch1_out) channel1(k*2 -1) = ch1_out(k); channel2(k*2 -1) = ch2_out(k); end % encontre a transformada inversa y1 = conv(channel1, [-1, -1]);

Weeks Apendice E.indd 390

20/06/12 10:38

Respostas para Exercícios Selecionados

391

y2 = conv(channel2, [-1, 1]); % Some o resultado. Ajuste o tamanho extra. y = y1(1:length(x)) + y2(1:length(x)); plot(1:length(x), x, 'b', 1:length(y), y, 'g') Ao examinarmos a relação entre y e x, descobrimos que y[n] = −2x[n].

7. Vimos um padrão para um CQF, no qual os coeficientes de um filtro são alternadamente negativados e espelhados, de modo que, se conhecermos os coeficientes de um filtro, poderemos encontrar os coeficientes para os outros três. Isso funciona de modo generalizado? Isso funcionaria para a = 3, b = 1 na Figura 8.5? Resposta: Não, isso não funciona de modo generalizado. Não apenas o padrão é importante – os valores do filtro são escolhidos cuidadosamente. Não podemos simplesmente utilizar valores quaisquer para os coeficientes de filtro.

9. Escreva uma função que retorne a primeira oitava, transformada de wavelet de Daubechies de 4 coeficientes para um dado sinal. Inclua saídas passa-baixo e passa-alto. Resposta: Precisamos convolver os coeficientes de Daubechies com a entrada e, em seguida, subamostrar os resultados. No código a seguir, ch1_out é a saída passa-baixo, enquanto ch2_out é a saída passa-alto. % obtenha os coeficientes d0 = (1-sqrt(3))/(4*sqrt(2)); d1 = -(3-sqrt(3))/(4*sqrt(2)); d2 = (3+sqrt(3))/(4*sqrt(2)); d3 = -(1+sqrt(3))/(4*sqrt(2)); LPF = [ d0, -d1, d2, -d3 ]; HPF = [ d3, d2, d1, d0 ]; % encontre a transformada direta channel1 = conv(x, LPF); channel2 = conv(x, HPF); % subamostre ch1_out = channel1(2:2:length(channel1)); ch2_out = channel2(2:2:length(channel2));

11. Para a transformada de Haar exibida na Figura 8.5, utilize valores a = b = ½ (sem subamostragem), encontre os sinais z, w e y, dada uma entrada x = {6, 1, 3, 7, 2, 5, 8, 10}. Resposta: x = [6, 1, 3, 7, 2, 5, 8, 10]; z = conv(x, [0.5, -0.5]); w = conv(x, [0.5, 0.5]); y1 = conv(z, [-0.5, 0.5]); y2 = conv(w, [0.5, 0.5]); y = y1 + y2; Vemos que y é o mesmo sinal que x, exceto pelo fato de possuir um zero extra em cada extremidade.

13. Para uma DWT de quatro oitavas, suponha que a entrada tenha 1024 amostras. Quão extensas as saídas de detalhe seriam? Quão extensas as saídas de aproximação seriam? E se a sub/sobreamostragem não fosse utilizada? Para simplificar, você pode considerar que as saídas e as entradas do filtro possuem a mesma extensão. Resposta: A extensão efetiva das saídas depende do tamanho do filtro, ou seja, o filtro db4 acrescenta mais algumas saídas em relação ao filtro db2. Em vez disso, responderemos em termos de padrão geral. Com os subamostradores, cada oitava possui (aproximadamente) metade das saídas de detalhe que a oitava antes dela. Portanto, teríamos:

Weeks Apendice E.indd 391

20/06/12 10:38

392

APÊNDICE E

As saídas aproximadas seriam do mesmo tamanho que os detalhes. Contudo, não precisamos manter as aproximações da primeiras três oitavas. Deste modo, o número total de saídas é igual a ⬇ 64 + 64 + 128 + 256 + 512, ou 1024 saídas. Se a sub/sobreamostragem não fosse utilizada, cada canal produziria o mesmo número de entradas. O número total de saídas seria igual a ⬇ 1024 + 1024 + 1024 + 1024 + 1024, ou 5120 saídas.

15. Afirmamos anteriormente que o filtro espelhado em quadratura não funcionará com quatro coeficientes, ou seja, h0 = g0 = {a, b, c, d}, h1 = {a, −b, c, −d}, g1 = −h1. Mostre analiticamente que isso não funcionará. Ou seja, que restrições existem sobre os valores a, b, c e d para que se obtenha uma reconstrução perfeita? Resposta: Não consideraremos sub/sobreamostradores como parte desta resposta, pois seu efeito somente escalonará nossos resultados. Para encontrarmos o efeito do canal superior na entrada, convolvemos h0 com g0 e calculamos a transformada z do resultado. a b c d a b c d ---------aa ab ac ad ab bb bc bd ac bc cc cd ad bd cd dd -------------------Nosso resultado é aa, 2ab, 2ac + bb, 2(ad + bc), 2bd + cc, 2cd, dd. A transformada z é:

aa + (2ab)z−1 + (2ac + bb)z−2 + (2(ad + bc))z−3 + (2bd + cc)z−4 + (2cd)z−5 + (dd)z−6 Agora encontramos o efeito do canal inferior na entrada, convolvemos h1 com g1 e calculamos a transformada z do resultado. a -b c -d -a b -c d --------------aa ab -ac ad ab -bb bc -bd -ac bc -cc cd ad -bd cd -dd ---------------------------A transformada z do resultado é:

Ao somarmos as duas transformadas z para obtermos Y(z), temos:

Cada termo alternado foi cancelado. Nosso objetivo é obter Y(z) com exatamente um termo. Para que esta exigência seja satisfeita, dois dos três termos devem ser iguais a zero, ou seja, ab = 0 e cd = 0. Só podemos obter ab = 0 se a ou b for igual a zero. Assim, concluímos que isso não funcionará, exceto para o caso trivial em que dois dos quatro coeficientes são iguais a zero. Não seria diferente a partir de um filtro com dois taps.

Capítulo 9 1. Esse capítulo utilizou a função Chapéu Mexicano. Na função mycwt, o que você precisaria mudar para utilizar outra wavelet? Resposta: A principal mudança seria a linha que chama a função Chapéu Mexicano (mexican hat). psi = c*mexicanhat((t-real_bu)/real_as); Embora mantivéssemos a lista de parâmetros como (t-real_bu)/real_as, a chamada poderia ser para qualquer função, incluindo aquela que definimos. Ou seja, podemos ter que definir a função wavelet que desejamos utilizar como uma função em separado.

Weeks Apendice E.indd 392

20/06/12 10:38

Respostas para Exercícios Selecionados

393

3. O código a seguir mostra metade de um Chapéu Mexicano: t = 0:0.01:10; mexhat = (1 - t.*t) .* exp(-t.*t/2); plot(t, mexhat) Como você pode alterá-lo para que passe a mostrar a função Chapéu Mexicano completa? Como você pode alterá-lo de modo a deslocar o centro até o ponto 6? Resposta: O problema encontra-se em nosso índice, t, que possui apenas valores positivos. Podemos ajustar a primeira linha de modo que t varie de −10 a 10. t = -10:0.01:10; Ou poderíamos alterá-la para que varie de −5 a 5. Ao encontrarmos a lista mexhat e traçarmos os resultados, obtemos a função Chapéu Mexicano completa. O deslocamento desta função pode ser obtido alterando-se o parâmetro t na definição de mexhat, substituindoa por (t-6). t = -10:0.01:10; mexhat = (1 - (t-6).*(t-6)) .* exp(-(t-6).*(t-6)/2); plot(t, mexhat) O traçado mostra a função Chapéu Mexicano centralizada em torno de t = 6. Note que isso é muito melhor do que simplesmente fraudar os resultados alterando o comando plot.

5. O livro Wavelet Tour, de Mallat, fornece uma versão alternativa da função Chapéu Mexicano, como a seguir: sigma = 1; mexhat = exp(-t.*t/(2*sigma*sigma)) * ... 2 .*(t.*t/(sigma*sigma) -1) / ... (pi^(1/4)*sqrt(3*sigma)); Como ela se compara ao Chapéu Mexicano definido como (1 − t.*t) .* exp(−t.*t/2)? A função de Mallat possui soma zero? O que você observa ao examinar esta função com valores sigma iguais a 0.5, 1 e 2? Resposta: A representação gráfica dessa função, comparada à da função (1 − t.*t) .* exp(−t.*t/2) mostra que a versão de Mallat possui a mesma forma, apenas rebatida em torno do eixo x (de cabeça para baixo), ao menos quando sigma = 1. O código a seguir compara as duas, com a versão de Mallat exibida em vermelho. Ao examinarmos a equação (ou o gráfico), constatamos que a função alternativa é escalonada por uma constante aproximadamente igual a 0.9. t = -5:0.01:5; sigma = 1; mexhat = exp(-t.*t/(2*sigma*sigma)) * ... 2 .*(t.*t/(sigma*sigma) -1) / ... (pi^(1/4)*sqrt(3*sigma)); mexhat2 = (1 - t.*t) .* exp(-t.*t/2); plot(t,mexhat, 'r', t, mexhat2, 'b'); sum(mexhat) Ao calcularmos a soma, obtemos −0.0032. Mas devemos suspeitar que ela seja igual a zero. Se mudarmos a definição para t, encontraremos uma soma zero. >> t = -10:0.01:10; >> sigma = 1; >> mexhat = exp(-t.*t/(2*sigma*sigma)) * ... 2 .*(t.*t/(sigma*sigma) -1) / ... (pi^(1/4)*sqrt(3*sigma)); >> sum(mexhat) ans = 5.9309e-15 Vemos que isso efetivamente nos fornece uma soma zero.

Weeks Apendice E.indd 393

20/06/12 10:38

394

APÊNDICE E

O código a seguir investiga os diferentes valores sigma. t = -5:0.01:5; sigma = 0.5; mexhat05 = exp(-t.*t/(2*sigma*sigma)) * ... 2 .*(t.*t/(sigma*sigma) -1) / ... (pi^(1/4)*sqrt(3*sigma)); sigma = 1; mexhat1 = exp(-t.*t/(2*sigma*sigma)) * ... 2 .*(t.*t/(sigma*sigma) -1) / ... (pi^(1/4)*sqrt(3*sigma)); sigma = 2; mexhat2 = exp(-t.*t/(2*sigma*sigma)) * ... 2 .*(t.*t/(sigma*sigma) -1) / ... (pi^(1/4)*sqrt(3*sigma)); plot(t, mexhat05, 'r', ... t, mexhat1, 'b', ... t, mexhat2, 'g'); A partir do traçado, vemos que um valor sigma de 0.5 produz um chapéu mais estreito, em comparação a um valor sigma de 1. Ele também possui maior amplitude. Quando sigma possui o valor 2, o Chapéu é alongado, com uma faixa variando de aproximadamente 0.3 a − 0.6.

7. Calcule a multiplicação ponto por ponto da onda triangular f1 (definida a seguir) pela função Chapéu Mexicano. A soma desta multiplicação é igual a zero? t = -10:0.02:10; f1 = f_of_t(t, 'triangle'); plot(t,f1) Se você utilizar f2 em vez de f1, a multiplicação ponto por ponto pela função Chapéu Mexicano terá soma igual a zero? Por que ou por que não? f2 = f_of_t(t, 'triangle')-2.5; Resposta: O código a seguir calculará a multiplicação ponto por ponto em uma variável chamada result. O comando plot abaixo mostra a função original e o Chapéu Mexicano. Vemos a partir do gráfico resultante que essas duas funções se sobrepõem, mas não estão centralizadas. Assim, muito embora elas sejam simétricas, o resultado não é. Não esperamos que a soma seja igual a zero. t = -10:0.02:10; f1 = f_of_t(t, 'triangulo'); mexhat = (1 - t.*t) .* exp(-t.*t/2); plot(t, f1, 'r', t, mexhat, 'b') result = f1 .* mexhat; sum(result) A soma resulta em −50.0013. Se utilizarmos alternativamente a função f2, veremos que as duas funções simétricas não estão alinhadas ao longo do eixo x. Portanto, o resultado não será simétrico e a soma não será igual a zero. f2 = f_of_t(t, 'triangulo')-2.5; result = f2 .* mexhat; plot(t, f2, 'r', t, mexhat, 'b') sum(result) A soma é a mesma de antes.

9. Defina sua própria função e encontre a transformada contínua de wavelet correspondente com a wavelet Chapéu Mexicano.

Weeks Apendice E.indd 394

20/06/12 10:38

Respostas para Exercícios Selecionados

395

Resposta: O código a seguir define uma função (myf) como uma senoide e, em seguida, calcula a CWT. Os resultados são mostrados como uma imagem. t = -10:0.02:10; myf = 2*cos(2*pi*t/5); W = mycwt(myf, 50); image(W) Também poderíamos utilizar alternativamente a função cwt do MATLAB.

Capítulo 10 1. Suponha que a variável x contenha informações sonoras armazenadas como valores double. Que efeito o comando sound(x*.7) possui? Que efeito o comando sound(x*1.1) possui? Resposta: A multiplicação do vetor x por uma fração reduzirá cada valor nele contido. O som resultante terá um volume baixo. A utilização de um fator de multiplicação 1.1 aumentará o som. Ele soará mais alto, mas os valores sonoros poderão ultrapassar +1 e, portanto, ser podados.

3. Como você pode concatenar duas listas de som de modo a reproduzi-las uma após a outra? O que acontecerá se elas tiverem dois canais cada uma? Resposta: Considere que disponhamos de duas listas sonoras já na memória, x1 e x2. O código a seguir encontrará a concatenação de duas listas sonoras, considerando-se que ambas possuam dois canais. [r1, c1] = size(x1); [r2, c2] = size(x2); y = x1; y(r1+1:r1+r2, 1:c2) = x2; sound(y, fs) O som deve ser armazenado na forma de vetores de coluna. Se o armazenamento ocorresse na forma de vetores de linha, seria preciso alterar a linha antes do comando sound.

5. Converta um sinal sonoro para o domínio da frequência, desloque o sinal copiando os dados no domínio da frequência para novos índices e converta-o de volta. Você pode conseguir isso mudando a frequência de amostragem? O que acontecerá se você reverter a ordem dos índices antes de converter de volta? Resposta: Leremos um arquivo sonoro e o armazenaremos na variável x. Considerando que x possua dois canais, com o som em vetores de coluna, utilizaremos somente o primeiro canal e obteremos o resultado como um vetor de linha. [x, fs, b] = wavread('test_file.wav'); x = x(:,1).'; X = fft(x); % Desloque os indices por 50 Xshifted = [X(50:length(X)), X(1:49)]; % converta para o dominio do tempo xshifted = ifft(Xshifted); % reproduza o resultado sound(real(xshifted), fs) O código anterior reproduz o som com um ruído semelhante a uma “pulsação”. Isso desloca o espectro inteiro, em vez de apenas a primeira metade. Uma vez que as metades seriam simétricas, faz sentido trabalhar somente na primeira metade e espelhar os resultados. % Desloque os indices somente na primeira metade Xhalf = X(1:ceil(length(X)/2)); firstHalf = [Xhalf(50:length(Xhalf)), Xhalf(1:49)]; % agora espelhe esta metade na segunda metade secondHalf = firstHalf(length(Xhalf):-1:1);

Weeks Apendice E.indd 395

20/06/12 10:38

396

APÊNDICE E

Xshifted = [firstHalf, secondHalf]; xshifted = ifft(Xshifted); sound(real(xshifted), fs) Isso ainda soa estranho. O ruído pulsante se foi, mas o som possui um estranho problema de temporização. Não, não seremos capazes de replicar isso simplesmente alterando a frequência de amostragem. (Embora isso possa funcionar se o sinal for uma frequência única.) Agora vamos inverter o espectro: % Desloque os indices somente na primeira metade midpoint = ceil(length(X)/2); firstHalf = X(midpoint:-1:1); % agora espelhe isso na segunda metade secondHalf = firstHalf(midpoint:-1:1); Xreversed = [firstHalf, secondHalf]; xreversed = ifft(Xreversed); sound(real(xreversed), fs) Os sons são quase inaudíveis e a frequências muito altas.

7. Vimos anteriormente um código que gira uma imagem no sentido horário. Modifique-o para girar a imagem no sentido anti-horário. Resposta: Para encontrar a resposta para um problema como este, tente primeiramente por conta própria utilizando uma pequena matriz (digamos, 4 × 4). Uma vez obtido o resultado desejado, tente isolar uma linha (ou coluna) da solução e depois replicá-la para as outras linhas (ou colunas). x = imread('dog256x256.gif'); % Gire a imagem e exiba-a % Isso deve funcionar para qualquer imagem, independentemente das dimensoes [MAX_ROWS, MAX_COLS] = size(x); for r=1:MAX_COLS cclockwise(:,r) = x(r, MAX_ROWS:-1:1); end imshow(cclockwise);

9. O que aconteceria se a função de busca do solucionador do Sudoku não incluísse a palavra chave function em sua definição? Resposta: O programa não funcionaria. Obteríamos uma mensagem de erro do MATLAB, pois é preciso alterar cada chamada para somente o nome, sem parâmetros, ou seja, sudoku_search em vez de sudoku_search(puzzle). E, mesmo que consideremos que tal medida já tenha sido tomada, ainda assim o programa não funcionará. A palavra-chave function significa que o computador criará suas próprias cópias dos parâmetros e as descartará após a conclusão da função. A remoção desta palavra-chave significa que haverá somente uma cópia do quebra-cabeça. Uma vez que o quebra-cabeça possua um palpite agregado a ele, os programas não serão capazes de desfazer o palpite posteriormente.

11. Durante a utilização da função window para projetar coeficientes de filtro, muitas janelas são suportadas. Faça experiências com as janelas gausswin, hamming, rectwin e triang para determinar qual janela produz os melhores resultados para um filtro FIR passa-baixo. Resposta: Podemos modificar o programa filter_test2.m presente no CD-ROM para descartar aquilo de que não precisamos e para acrescentar diversas janelas. Primeiramente, repetiremos a parte inicial do programa, onde criamos os coeficientes de filtro não janelados, c2. Examine o programa filter_test3.m no CD-ROM. low_pass_end = 150; taps = 51; % crie uma funcao impulso MAXLEN = 1000; %1950; imp = zeros(1, MAXLEN); imp(50) = 1; % simule a resposta que desejamos no dominio da frequencia

Weeks Apendice E.indd 396

20/06/12 10:38

Respostas para Exercícios Selecionados

397

fx = zeros(1, MAXLEN); fx(1:low_pass_end) = 1; % passa-baixo % inclua a imagem espelhada do passa-baixo fx((MAXLEN-low_pass_end):MAXLEN) = 1; % converta para o dominio do tempo x = ifft(fx); % desloque os dados de frequencia mid_point = floor(MAXLEN/2); x2 = [x(mid_point+1:MAXLEN), x(1:mid_point)]; half_taps = floor((taps-1)/2); % obtenha o numero solicitado de coeficientes de filtro b2 = x2(mid_point - half_taps : mid_point + half_taps); % utilize o sinal com abs para nos fornecer uma funcao como sinc for k=1:length(b2) if (sign(b2(k)) < 0) c2(k) = -abs(b2(k)); else c2(k) = abs(b2(k)); end end Agora podemos gerar diversas janelas diferentes e encontrar os coeficientes janelados. % obtenha diferentes valores de janela w1 = window(@gausswin, taps); w2 = window(@hamming, taps); w3 = window(@rectwin, taps); w4 = window(@triang, taps); % agora combine a janela com coeficientes de filtro coeffs1 = w1.' .* c2; coeffs2 = w2.' .* c2; coeffs3 = w3.' .* c2; coeffs4 = w4.' .* c2; Depois disso, convolvemos os coeficientes com a função impulso, calculamos a FFT e guardamos o valor máximo (para uso posterior no traçado). y1 = conv(imp, coeffs1); y2 = conv(imp, coeffs2); y3 = conv(imp, coeffs3); y4 = conv(imp, coeffs4); Y1 = fft(y1); Y2 = fft(y2); Y3 = fft(y3); Y4 = fft(y4); maxY1 = max(abs(Y1)); maxY2 = max(abs(Y2)); maxY3 = max(abs(Y3)); maxY4 = max(abs(Y4)); A seguir, traçamos os resultados. Visto que alguns dos filtros produzem respostas de magnitude em frequência próximas, utilizaremos um traçado logarítmico de modo a visualizar as pequenas diferenças. half = 1:ceil(length(y2)/2); xaxis2 = (1:length(half))*100/length(half); plot(xaxis2, 20*log(abs(Y1(half))/maxY1), 'k', ... xaxis2, 20*log(abs(Y2(half))/maxY2), 'g', ... xaxis2, 20*log(abs(Y3(half))/maxY3), 'r', ... xaxis2, 20*log(abs(Y4(half))/maxY4), 'b');

Weeks Apendice E.indd 397

20/06/12 10:38

398

APÊNDICE E

A partir do traçado, podemos constatar imediatamente que os traçados vermelho e azul não estão muito bons. Eles correspondem às janelas retangular e triangular. Os outros dois estão bons, e podemos perceber um compromisso clássico no projeto de filtros. Enquanto a janela Gaussian (preta) possui lobos laterais menores, a janela Hamming (verde) possui um lobo principal mais estreito. A resposta de frequência da janela Gaussian parece ser ligeiramente melhor que a resposta da janela Hamming.

13. Encontrar o número de valores distintos constitui basicamente uma contagem. Uma forma de fazê-lo na forma de um algoritmo consiste em ordenar os valores e eliminar quaisquer duplicatas – o número de valores restantes corresponde ao número de valores distintos. Por exemplo, na série {7, 3, 5, 1, 3, 3, 5, 4, 1, 7, 4, 5} há somente cinco valores distintos: {1, 3, 4, 5, 7}. A mais próxima potência de 2 maior que (ou igual a) isso é 8, ou 23. Assim, podemos atribuir a cada valor um código diferente de 3 bits. O que queremos fazer é armazenar um sinal em menos espaço. Ou seja, poderíamos representar esses valores com 3 bits cada. Esta não é a forma mais eficiente de armazenar estes valores, mas seu propósito é dar a você uma ideia de como a compressão funciona. (A codificação de Huffman é mais eficiente.) Suponha que tenhamos o seguinte sinal de exemplo:

x[n] = {0, −1, 9, −1, 9, 9, 0, 3, −1, 3, 0, 0}. Ao armazenarmos esses valores como inteiros, provavelmente utilizaríamos 16 bits para armazenar cada valor, para um total de 192 bits. Isso, no entanto, seria um desperdício em termos da quantidade de espaço. (a) Quantos valores distintos há no sinal de exemplo anterior, x[n]? Resposta: Há quatro (22) valores distintos {0, −1, 3, 9}, de modo que precisaríamos de 2 bits para armazenar cada um. Assim, precisamos de 12 × 2 = 24 bits para armazenar a sequência anterior, mais uma tabela que nos possibilite saber como decodificar os bits. (b) Decida quais padrões de bits devem representar cada valor distinto (formando uma tabela de pesquisa). Resposta: Considere 0 = 00, −1 = 01, 3 = 10, 9 = 11 (isso não é o mesmo que armazená-los em binário!). Além disso, existem diversas outras maneiras de gerar esta tabela. O mapeamento aqui é um tanto arbitrário. (c) Mostre como o sinal de exemplo completo anterior (x[n]) seria caso estivesse codificado. Lembre-se de que também queremos ser capazes de desfazer isso – fornecidos o padrão de bits e a tabela de pesquisa, devemos ser capazes de obter x[n] de volta. Resposta: O sinal anterior seria armazenado como: 00 01 11 01 11 11 00 10 01 10 00 00 (d) Quantos bits o sinal codificado ocupa? E se você incluísse a tabela de codificação? Resposta: O sinal codificado requer 24 bits. Também precisaríamos armazenar a tabela de decodificação. Precisamos de uma extensão de tabela – digamos, 16 bits – para tal. Poderíamos utilizar 16 bits por valor, depois um byte para codificar o padrão e a contagem de bits (ou seja, 0010 0000 para o primeiro valor). Total: 16 bits + 4 (16 + 8) + 24 = 136 bits. Este é um pequeno exemplo no qual a tabela de codificação ocupa muito espaço, mas somos capazes de salvá-la em 70% do espaço.

Weeks Apendice E.indd 398

20/06/12 10:38

Glossário

F

ADC: conversor digital-para-analógico. Um dispositivo que amostra valores contínuos e a partir deles produz valores discretos. Amostragem: o processo que converte um sinal analógico em uma representação digital. Amostragem crítica (critical sampling): amostragem a exatamente duas vezes a largura de banda, rápido o suficiente. Amostragem de banda limitada (bandpass sampling): amostragem a menos de o dobro da frequência máxima, mas no mínimo com o dobro da largura de banda. Amostragem passa-baixo: considera que a largura de banda começa em 0 e vai até a frequência máxima. Amplitude: uma quantidade sem unidades, que pode ser positiva ou negativa. Atenuar: reduzir substancialmente determinadas frequências. Banco de filtros: um par de filtros utilizado com a transformada discreta de wavelet, um passa-baixo e um passa-alto. Banco de filtros de dois canais: termo genérico para a combinação de filtros de análise e de síntese. Causal: um sistema que utiliza somente entradas correntes ou prévias. Codificação de sub-banda: o tipo de operação produzida por um banco de filtros na transformada de wavelet, onde filtros complementares dividem o sinal em um subsinal de componentes de baixa frequência e um de componentes de alta frequência. Componente DC: derivado de corrente direta, este termo corresponde à amplitude a 0 Hz em uma representação de um sinal no domínio da frequência. Componente de frequência: uma (das muitas) senoides que compõem um sinal. Conjugado complexo: um número idêntico a outro complexo, exceto pelo sinal diferente para a parte complexa. Por exemplo, 1 + 2j e 1 − 2j são conjugados complexos. DAC: conversor digital-para-analógico, um dispositivo que preenche os vazios entre as amostras e a partir delas produz valores contínuos. Dobramento (folding): um tipo de sobreposição espectral na qual a frequência de amostragem encontrase entre a largura de banda e duas vezes a largura de banda. Escalar: um único número isolado, tal como 3.1. Compare com vetor. Espectro: o traçado de frequência de um sinal. Fasor: um número na forma rej␪ no plano complexo. Imagine uma seta direcionada a partir da origem. Um fasor girante é aquele no qual o ângulo, ␪, muda com o tempo, de forma muito parecida com os ponteiros de um relógio analógico se este girasse no sentido anti-horário. Função de transferência (H(z)): uma função de entrada/saída que descreve o comportamento de um filtro. Ela se baseia na transformada z dos coeficientes de filtro. Inserção de amostras nulas (ou de valor zero): acréscimo de zeros no sinal no domínio do tempo. Invariante no tempo: uma propriedade de alguns sistemas na qual um deslocamento na entrada produz um deslocamento correspondente na saída. 399

Weeks Apendice F.indd 399

20/06/12 10:46

400

APÊNDICE F

Largura de banda (bandwidth): faixa de frequências do sinal. Linear: uma propriedade de alguns sistemas (como um filtro FIR ou IIR) em que a relação de saída apresenta uma soma de entradas multiplicada por constantes. Lista (array): uma lista de números unidimensional, tal como {1, 2, 3}. Também chamada de vetor. LTI: linear e invariante no tempo. Magnitude: uma quantidade sem unidades, sempre positiva. Matriz (plural matrizes): um grupo de valores multidimensional. Uma imagem constitui um grupo de valores de pixel bidimensional e pode ser considerada uma matriz. Multirresolução: se a transformada/transformada inversa funcionar uma vez, poderemos continuar executando a transformada indefinidamente. Posteriormente, executamos a transformada inversa uma vez para cada vez que executamos a transformada direta. Normalizado: escalonamento executado em coeficientes de filtro de modo que nenhum escalonamento seja necessário no final. Ou seja, um filtro e um filtro inverso devem gerar os mesmos valores que foram aplicados. Se os coeficientes de filtro não estivessem normalizados, a saída consistiria na entrada multiplicada por uma constante. Número complexo: uma extensão de um número, um número complexo possui uma parte real e uma parte imaginária – por exemplo, 3 − 4j. Oitava: um nível de resolução na transformada discreta de wavelet. Os detalhes na oitava n são menos precisos do que aqueles na oitava n − 1. Ortogonal: em ângulo reto (bidimensional). Mais formalmente (e para dimensões maiores), a ortogonalidade é a propriedade na qual o produto interno das bases de coordenadas é igual a zero. Ortonormal: ortogonal e normalizado. Passa-alto: um filtro passa-alto permite a passagem das frequências altas. Passa-baixo: um filtro passa-baixo permite a passagem de frequências baixas. Período: O intervalo de tempo antes que um sinal (periódico) se repita. Pixel: forma reduzida para picture element, refere-se aos menores pontos que compõem uma tela de exibição, tal como em uma televisão. Este termo também refere-se à cor (ou ao número correspondente à cor) para um pixel, como na expressão valor de pixel. Polo: onde o denominador torna-se 0 em uma função de transferência (a resposta de frequência de um filtro). Ponto fixo: uma representação binária na qual um número especificado de bits contém as partes inteira e fracionária, com uma vírgula aritmética implícita. Reconstrução: converte um sinal digital de volta a analógico (também empregado para significar a aplicação de uma transformada inversa). Região de convergência (RoC): área na qual a transformada z converge, tipicamente no interior do círculo unitário. Resposta ao impulso: (também h[n]) mostra o comportamento de um filtro diante de uma única entrada diferente de zero. Para filtros FIR, isso nos mostra o efeito que o filtro exerce sobre as frequências de um sinal de entrada. Resposta de magnitude em frequência (FMR): as magnitudes relativas de frequências em um traçado de um sinal no domínio da frequência. Resposta finita ao impulso (FIR): este tipo de filtro fornece um número finito de saídas (resposta) diferentes de zero para uma entrada de função impulso. Ele não utiliza realimentação; Resposta infinita ao impulso (IIR): este tipo de filtro utiliza realimentação; portanto poderia apresentar um número infinito de saídas (resposta) diferentes de zero para uma entrada de função impulso.

Weeks Apendice F.indd 400

20/06/12 10:46

Glossário

401

Ruído: qualquer conteúdo de frequência não desejado. Senoide: um termo genérico para referir-se à função seno ou cosseno. Sinal: um fenômeno variável e mensurável, frequentemente uma quantidade física que varia com o tempo. Sinal harmônico: diversas senoides somadas, cada qual com um múltiplo inteiro de uma frequência fundamental. Sistema: algo que executa uma operação em um sinal. Sobreamostragem: coleta de amostras com mais frequência do que o necessário. Sobreposição espectral (aliasing): replicações, causadas por amostragem, que interferem no sinal. Subamostragem: ocorre quando não coletamos amostras com a frequência necessária. Tamanho de palavra: o número de bits que um microprocessador acessa em uma única operação. Transformada: a operação que o sistema executa. Transformada contínua de Fourier (CFT): uma versão da transformada de Fourier para sinais contínuos. Aqui nós a utilizamos como uma abstração matemática, útil para entender a teoria. Transformada contínua de Wavelet (CWT): uma transformada na qual o sinal é integrado a uma função wavelet mãe deslocada e escalonada. Em termos práticos, um computador a executa como uma transformada de discreta de wavelet com muita redundância. Transformada de Fourier (FT): um modo de converter dados no domínio do tempo para o domínio da frequência. Transformada discreta de Fourier (DFT): uma forma de converter dados discretos no domínio do tempo em dados discretos no domínio da frequência. A transformada rápida de Fourier (FFT) é mais eficiente e produz os mesmos resultados. Transformada discreta de wavelet (DWT): uma transformada na qual um sinal é convolvido com um par de filtros – um filtro de escalonamento passa-baixo e um filtro de wavelet passa-alto. Transformada rápida de Fourier (FFT): uma forma rápida de converter dados discretos no domínio do tempo em dados discretos no domínio da frequência. Ela produz os mesmos resultados que a transformada discreta de Fourier. O MATLAB disponibiliza esta função. Transformada z: transforma dados no domínio do tempo para o domínio da frequência. Sob determinadas condições, ela reduz-se à transformada de Fourier. Unidade de multiplicação e acumulação (MAC – multiply accumulate cell): uma estrutura regular que pode ser utilizada para a implementação de filtros em hardware. Vazamento DFT: um problema de resolução da transformada discreta de Fourier (DFT) no qual a informação de frequência é espalhada por diversas frequências. Vetor: uma lista de números unidimensional, tal como {1, 2, 3}. Ela também é chamada de uma lista. O termo vetor também é empregado para referir-se a uma seta direcionada no espaço bidimensional (ou maior). Wavelet: literalmente, uma onda de pequenas dimensões (“ondeleta”). Trata-se de uma função especial utilizada na transformada de wavelet. Para o caso discreto, há uma função de escalonamento correspondente. Zero: onde o numerador torna-se 0 em uma função de transferência (a resposta de frequência de um filtro).

Weeks Apendice F.indd 401

20/06/12 10:46

Weeks Apendice F.indd 402

20/06/12 10:46

Referências

[1] [2] [3] [4]

[5] [6] [7] [8] [9] [10] [11] [12] [13] [14] [15] [16] [17] [18] [19] [20] [21] [22] [23] [24] [25] [26] [27] [28] [29] [30] [31] [32]

[33]

G

S. Mallat, “A Theory for Multiresolution Signal Decomposition: The Wavelet Representation”, IEEE Pattern Analysis and Machine Intelligence, vol. 11, nº 7, pp. 674–693, 1989. I. Daubechies, Ten Lectures on Wavelets. Montpelier, Vermont: Capital City Press, 1992. G. Strang e T. Nguyen, Wavelets and Filter Banks. Wellesley-Cambridge Press, 1997. R. R. Coifman e M. V. Wickerhauser, “Wavelets and Adapted Waveform Analysis”, em Proceedings of Symposia in Applied Mathematics (I. Daubechies, ed.), pp. 119–153, Providence, Rhode Island: American Mathematics Society, 6–9 de novembro, 1993. Volume 47. The Learning Company, Inc., Compton’s Interactive Encyclopedia. Cambridge, Massachusetts: Simon e Schuster, 1995. D. Goldberg, “What Every Computer Scientist Should Know About Floating-point Arithmetic”, Computing Surveys, pp. 171–264, março de 1991. J. H. McClellan, R. W. Schafer e M. A. Yoder, DSP First: A Multimedia Approach. Prentice-Hall, 1998. B. B. Hubbard, The World According to Wavelets. Wellesley, Massachusetts: A. K. Peters, 1996. R. Nave, http://hyperphysics.phy-astr.gsu.edu/HBASE/hph.html, HyperPhysics. Department of Physics and Astronomy, Georgia State University, 2005. T. Bose, Digital Signal and Image Processing. John Wiley & Sons, 2004. P. Linz e R. L. C. Wang, Exploring Numerical Methods: An Introduction to Scientific Computing Using MATLAB. Jones and Bartlett Publishers, 2003. R. Lyons, Understanding Digital Signal Processing. Addison-Wesley, 1997. S. W. Smith, Digital Signal Processing: A Practical Guide for Engineers and Scientists. Newnes, 2003. R. E. Walpole e R. H. Myers, Probability and Statistics for Engineers and Scientists, Terceira Edição. New York: MacMillan Publishing Company, 1985. E. C. Ifeachor e B. W. Jervis, Digital Signal Processing: A Practical Approach, Segunda Edição. Harlow, England: Prentice-Hall, 2002. C. B. Boyer, The History of the Calculus and Its Conceptual Development. New York: Dover Publications, Inc., 1959. C. J. Richard, Twelve Greeks and Romans Who Changed The World. Rowman & Littlefield, 2003. L. Gonick, The Cartoon History of the World, Volumes 1–7. New York: Doubleday, 1990. M. N. Geselowitz, “Hall of Fame: Heinrich Hertz”, IEEE-USA News & Views, novembro de 2002. . Princeton University Press, 1998. P. J. Nahin, An Imaginary Tale: The Story of S. P. Thompson e M. Gardner, Calculus Made Easy. New York: St. Martin’s Press, 1998. E. W. Swokowski, Elements of Calculus with Analytic Geometry. Boston, Massachusetts: Prindle, Weber and Schmidt, 1980. D. D. Andrew Bruce e H.-Y. Gao, “Wavelet Analysis”, IEEE Spectrum, pp. 26–35, outubro de 1996. E. Candés e M. Wakin, “An Introduction to Compressive Sampling”, IEEE Signal Processing Magazine, vol. 25, pp. 21–30, março de 2008. E. A. Lee e P. Varaiya, Structure and Interpretation of Signals and Systems. Addison-Wesley, 2003. R. W. Hamming, Digital Filters, Terceira Edição. Mineola, New York: Dover Publications, 1998. D. Sheffield, “Equalizers”, Stereo Review, pp. 72–77, abril de 1980. G. Kaiser, A Friendly Guide to Wavelets. Boston: Birkhauser, 1994. W. K. Chen, ed., The Electrical Engineering Handbook. Burlington, MA: Elsevier Academic Press, 2005. J. Ozer, “New Compression Codec Promises Rates Close to MPEG”, CD-ROM Professional, setembro de 1995. A. Hickman, J. Morris, C. L. S. Rupley e D. Willmott, “Web Acceleration”, PC Magazine, 10 de junho de 1997. W. W. Boles e Q. M. Tieng, “Recognition of 2D Objects from the Wavelet Transform Zero-crossing Representations”, em Proceedings SPIE, Mathematical Imaging, (San Diego, Califórnia), pp. 104–114, 11–16 de julho, 1993. Volume 2034. M. Vishwanath e C. Chakrabarti, “A VLSI Architecture for Real-time Hierarchical Encoding/decoding of Video Using the Wavelet Transform”, em IEEE International Conference on Acoustics, Speech, and Signal Processing (ICASSP ’94), (Adelaide, Austrália), pp. 401–404, 19–22 de abril, 1994. Volume 2.

403

Weeks Apendice G.indd 403

20/06/12 10:50

404

[34] [35] [36] [37] [38] [39]

[40] [41] [42] [43] [44] [45] [46] [47] [48] [49] [50]

[51]

Weeks Apendice G.indd 404

APÊNDICE G

M. Weeks e M. Bayoumi, “Discrete Wavelet Transform: Architectures, Design and Performance Issues”, Journal of VLSI Signal Processing, vol. 35, pp. 155–178, setembro de 2003. A. Haar, “Zur theorie der orthogonalen funktionensysteme”, Mathematische Annalen, vol. 69, pp. 331–371, 1910. M. J. T. Smith e T. P. Barnwell III, “Exact Reconstruction Techniques for Tree-structured Subband Coders”, IEEE Transactions on Acoustics, Speech, and Signal Processing, pp. 434–441, junho de 1986. S. Jaffard, Y. Meyer e R. D. Ryan, Wavelets Tools for Science & Technology. Philadelphia: Society for Industrial and Applied Mathematics (SIAM), 2001. H. Anton, Elementary Linear Algebra, Sexta Edição. New York: John Wiley & Sons, Inc., 1991. C. Chakrabarti e M. Vishwanath, “Efficient Realizations of the Discrete and Continuous Wavelet Transforms: From Single Chip Implementations to Mappings on SIMD Array Computers”, IEEE Transactions on Signal Processing, vol. 43, pp. 759–771, março de 1995. S. Mallat, Une exploration des signaux en ondelettes. Paris: Éditions de l’École Polytechnique, 2000. S. Mallat, “Zero-crossings of a wavelet transform”, IEEE Transactions on Information Theory, vol. 17, nº 4, pp. 1019–1033, 1991. S. Jaffard, Y. Meyer e R. D. Ryan, Wavelets Tools for Science & Technology. Philadelphia: Society for Industrial and Applied Mathematics (SIAM), 2001. C. Torrence e G. P. Compo, “A practical guide to wavelet analysis”, Bulletin of the American Meteorological Society, vol. 79, nº 1, pp. 61–78, 1998. E. Brannock e M. Weeks, “Edge Detection Using Wavelets”, em Proceedings of the 44th Annual ACM Southeast Conference, (Melbourne, Florida), pp. 649–654, 10–12 de março, 2006. S. Mallat e S. Zhong, “Characterisation of signals from multiscale edges”, IEEE Transactions Pattern Analysis and Machine Intelligence, vol. 14, nº 17, pp. 710–732, 1992. E. Brannock, M. Weeks e V. Rehder, “Detecting filopodia with wavelets”, em IEEE International Symposium on Circuits and Systems, (Kos, Greece), pp. 4046–4049, 21–24 de maio, 2006. M. Weeks e E. Brannock, “Demonstrating Edge Data Across Multiple Resolution Levels”, em Proceedings of the IEEE Southeast Conference, (Atlanta, Georgia), 5–8 de março, 2009. S.-W. Wu, “Additive Vector Decoding of Transform Coded Images”, IEEE Transactions on Image Processing, vol. 7, pp. 794–803, junho de 1998. O. K. Al-Shaykh e R. M. Mersereau, “Lossy Compression of Noisy Images”, IEEE Transactions on Image Processing, vol. 7, pp. 1641–1652, dezembro de 1998. E.-B. Fgee, W. J. Phillips e W. Robertson, “Comparing Audio Compression Using Wavelets with Other Audio Compression Schemes”, IEEE Canadian Conference on Electrical and Computer Engineering, vol. 2, pp. 698–701, 1999. T. Rabie, “Robust Estimation Approach for Blind Denoising”, IEEE Transactions on Image Processing, vol. 14, pp. 1755–1765, novembro de 2005.

20/06/12 10:50

Índice

A ADC, 399. Veja Conversor analógico-digital Aditiva, transformada, 19 Alfabeto grego, 357 Aliasing, 401 Alimentação direta, 73 Amostragem, 15, 149-168, 399 crítica, 149, 399 de banda limitada, 161-164, 399 de frequência e o espectro, 193, 194 de sinais reais, 22 equação, 362 passa-baixa, 399 reconstrução, 151 ruído de alta frequência e, 151 sobreposição espectral, 152 dobramento, 155 exemplo de, 153 localização de replicações após amostragem, 157-160 taxa de Nyquist, 160 Amplitude(s), 115, 116, 399 Análise da multiplicação matricial, 271 de Fourier, 141 multirresolução, 229 Analógico, 14 Ângulo(s) de radiano, 113 em gráficos de vetores unitários, 201 Aplicações com imagens, 317 com som, 315 compressão, 345 de imagem por conta própria, 349-351 de uma foto, 319 abordagem de baixo para cima, 319 nível adicional de simplificação, 321 processamento em lote, 321 utilização de imagens menores em uma página web, 322 de DWT, 272 detecção de borda, 329-332 execução da transformada discreta de wavelet bidimensional em uma imagem, 322 em cores, 325 em tons de cinza, 324 e reversão da transformada discreta de Wavelet, 326-329 projeto de filtro, 339 métodos de janelamento, 339 projetos de um filtro FIR, 341

resposta de magnitude em frequência de som, 337-339 solução recursiva de um quebracabeça Sudoku, 332-337 Aproximação de integral, equação, 361 Armazenamento de ponto flutuante, 6 Array, 400 Arredondamento, 54 Assobio como uma forma de controle remoto, 368 Atenuar, 399 Autocorrelação, 91 equação, 359 Autocovariância, 91 equação, 359 Avaliação de integrais, 292-296

B Banco de filtros, 399 de dois canais, 231, 399 Banda largura de, 400 limitada, amostragem de, 161-164 Base(s) 16, 3 hexadecimal, 3 ortonormais, 254-257 Bit, 7 Borda, detecção de, 329-332

C Cálculo da DFT direta e inversa sob a forma de operações matriciais, 203 programa para o, 204-206 de erro, 50 Causal, 399 Causalidade, 78-81 CFT. Veja Transformada contínua de Fourier Chips computacionais. Veja Circuitos integrados Circuitos integrados, 2 unitários, 113-114 Círculo, 112 Circunferência, 112 Codificação de sub-banda, 231, 399 Codificadores de sub-banda, 232 Coeficientes de filtro, 73 Coleta de amostras, 22 Comandos úteis, 63-64 Combinação de passa-baixas e passa-altas em uma matriz, 275 Como não traçar uma senoide, 44-46 Comparação de números com uma tolerância, 52-54 Complementary Metal Oxide Semiconductors (CMOS). Veja

Semicondutor Complementar de Óxido Metálico Complexo conjugado, 182 Componente DC, 134, 399 de frequência, 399 Compressão, 367 de imagem, 368 de uma foto, 319, 345 abordagem de baixo para cima, 319 de imagem por conta própria, 349-351 nível adicional de simplificação, 321 processamento em lote, 321 utilização de imagens menores em uma página web. 322 Condução de eletricidade, 2 Conjugado complexo, 399 Constante de Euler, 355 Contínua de Fourier, transformada, 401 equação, 360 de Wavelet, transformada, 287-314, 401 avaliação de integrais, 292-296 Chapéu Mexicano, 287-289 deslocamento de chapéu, 289-291 e escalonamento, 292 equação, 365 escalonamento de chapéu, 291 exemplos da, 299-301 em uma função quadrado, 303 exemplo analítico em uma função, 301 impulso, 302 multiplicação de uma função pelo Chapéu Mexicano, 296-299 programa para computar, 304 convolução, 305 reversão da, 306-309 Continuação de uma linha, 34 Convergência, região de, 400 Conversão de binário em decimal, 4 de decimal em binário, 5 entre representações, 4 Conversor analógico-digital, 151 de cores, 367 digital-analógico, 151 Convolução, 305 equação, 359 Correlação, 91 covariância, exemplo simples de, 93-95 cruzada, 91, 96-99 equação, 359 deslocamento do sinal, 99-102

405

Weeks indice.indd 405

05/07/12 11:16

406

Índice

equação, 359 exemplos de, 95, 96 expressão concisa para um valor de correlação aproximado, 92 programa de, cruzada, 102-106 Covariância cruzada, 91 equação, 359 Critério de Nyquist, 150 equação, 362 CWT. Veja Transformada contínua de Wavelet

D DAC, 399. Veja Conversor digital-analógico Dados bidimensionais, 362 Declaração(ões) condicionais, 32 if, 51 while, 51 Decomposição de um sinal em ondas, 244-253 Demonstração da equivalência da convolução no domínio do tempo em relação à multiplicação no domínio da frequência, 215, 216 Deslocamento de chapéu, 289-291 e escalonamento, 292 Desvio (bias), 7 Detecção de borda, 329-332 Detector de música, 368 de tanques, 367 Dez, 1 DFT. Veja Transformada discreta de Fourier direta e inversa, 184-187 Dicas de programação MATLAB, 56 Diferenciador musical, 367 Digital, 14 Signal Processing (DSP). Veja Processamento digital de sinais Direta de Fourier, transformada, funcionamento da, 194 cálculo da, direta e inversa sob a forma de operações matriciais, 203 programa para o, 204-206 exibição da, como ângulos em gráficos de vetores unitários, 201 quando a frequência de análise coincide com a real, 195-197 não coincide com a real, 197-199 transformada, 231 Discreta de Fourier, transformada, 171, 172, 227-285, 401 banco de filtros de dois canais, 231 bases ortonormais, 254-257 de Wavelet, 227-229, 401 equação, 364 decomposição de um sinal em ondas, 244-253 equação, 361 execução da DWT com matrizes, 271 análise da multiplicação matricial, 271

Weeks indice.indd 406

aplicação de DWT, 272 combinação de passa-baixas e passa-altas em uma matriz, 275 generalização da DWT com matrizes, 277-280 subamostragem com matrizes, 274 filtros com quatro coeficientes, 253, 254 em quadratura conjugada, 232 espelhados em quadratura, 232 multirresolução, 257-260 projeto de filtro wavelet, 253, 254 resposta de magnitude em frequência para uma wavelet, 281-283 sobreamostragem, 239 com Daubechies 4, 242-244 de dois coeficientes, 241 subamostragem, 239 com Daubechies 4, 242-244 de dois coeficientes, 241, 242 teoria da transformada de wavelet, 264-271 transformada de Haar, como a, afeta o ângulo de um ponto, 235 raio de um ponto, 234 equivale a uma rotação de 45o, 234 wavelet(s) de quatro coeficientes de Daubechies, 236-239 biortogonais, 260-264 inversa, 182-184 Dobramento, 399 DWT. Veja Transformada discreta de Fourier de Wavelet

E Efeitos de borda, 141 Elementos de processamento, 82 Equação(ões), 359 de Euler, 360 forma alternativa da, 123 Erro médio quadrático, 362 Escalar(es), 30, 399 Escalonamento de chapéu, 291 Espectro, 142-144, 399 Espelhado, 233 Estatística, equação, 362 Euler, constante de, 355 Execução da DWT com matrizes, 271 análise da multiplicação matricial, 271 aplicação de DWT, 272 combinação de passa-baixas e passa-altas em uma matriz, 275 generalização da DWT com matrizes, 277-280 subamostragem com matrizes, 274 da transformada discreta de Wavelet bidimensional em uma imagem, 322 em cores, 325 em tons de cinza, 324

e reversão da transformada discreta de Wavelet, 326-329 Exemplos aritméticos, 34-43 Exercícios de programação MATLAB, 57-63 Exibição da DFT como ângulos em gráficos de vetores unitários, 201 Expoente, 6

F Fases, equação, 361 Fasor, 117, 399 Fenômeno de Gibbs, 141 FFT. Veja Transformada rápida de Fourier Filtragem de ruído, 367 Filtro(s), 69-109 causalidade, 78-81 com quatro coeficientes, 253, 254 componentes, 71-73 correlação, 91 covariância cruzada, exemplo simples de correlação, 93-95 cruzada, 96-99 deslocamento do sinal, 99-102 exemplos de, 95-96 expressão concisa para um valor de, aproximado, 92 programa de, cruzada, 102-106 de dois canais, 231 de resposta finita ao impulso (FIR), 70, 87 em quadratura conjugada, 232 espelhados em quadratura, 232 estruturas do, FIR, 72-78 FIR, 87-89 invariância no tempo, 78-81 linearidade, 78-81 projeto de, 339 métodos de janelamento, 339 um filtro FIR, 341 resposta de frequência de filtros, 82-87 tendências de um, IIR simples, 89-91 unidade de multiplicação e acumulação, 81, 82 Wavelet, projeto de, 253, 254 Finite impulse response (FIR). Veja Filtros de resposta finita ao impulso FIR. Veja Resposta finita ao impulso Fluxo de eletricidade, 2 FMR. Veja Resposta de magnitude de frequência Fórmula de Euler, 117, 122, 123, 360 inversa de Euler, 123-125, 360 Fourier, transformada de, 169-208, 401 amostragem de frequência e o espectro, 193, 194 cálculo da DFT direta e inversa sob a forma de operações matriciais, 203 programa para o, 204-206 como a transformada z reduz-se à, 214 DFT direta e inversa, 184-187 discreta, 171, 172 no tempo, 171 equação, 360 funcionamento da DFT, 194 exibição da DFT como ângulos em gráficos de vetores unitários, 201 quando a frequência de análise coincide com a real, 195-197

05/07/12 11:16

Índice

não coincide com a real, 197-199 harmônicos e a, 189-193 inserção de amostras nulas (zero padding), 179 rápida, 171 teoria do deslocamento para a DFT, 180, 181 traçado do espectro, 174-179 transformada discreta de Fourier inversa, 182-184 vazamento espectral, 187-189 Frequência, 23 de amostragem, 22, 149 de análise, equação, 361 de som, resposta de magnitude em, 337-339 fundamental, 133 FT. Veja Transformada de Fourier Função(ões), 43 ceil, 55 Chapéu Mexicano, 287, 289 como a transformada de Fourier, 8 de transferência, 399 dwt, 228 fix, 55 floor, 54 round, 54 senoidais, 20 sinc, 70 Funcionamento da DFT, 194 quando a frequência de análise coincide com a real, 195-197 não coincide com a real, 197-199

G Generalização da DWT com matrizes, 277-280 Geometria e trigonometria, revisão, 111 Gibbs, fenômeno de, 141

H Haar, transformada de, 20, 232 afeta o ângulo de um ponto, 235 afeta o raio de um ponto, 234 como a, equivale a uma rotação de 45o, 234 Harmônicos e a transformada de Fourier, 189-193 Hertz, Heinrich, 113

I Identidades trigonométricas, 363 IEEE. Veja Institute of Electrical and Electronics Engineers 754, 6, 7 IIR. Veja Resposta infinita ao impulso Imagem(ns) aplicações com, 317 em cores, 325 em tons de cinza, 324 menores em uma página web, 322 Impulso resposta ao, 400 unitário, 77 Infinito, 4 Inserção de amostras nulas (zero padding), 179, 399 Institute of Electrical and Electronics Engineers (IEEE), 6

Weeks indice.indd 407

Integração em escala muito grande, 2 Integral, 361 Interpolação, 151 Intervalos de números, 31 Invariância no tempo, 78-81 Invariante no tempo, 399 Inversa, transformada, 231 Inversor, 2

J JPEG (Joint Photographic Experts Group), 169 Juros compostos, 120 simples, 120

L Laplace, transformada de, 19, 365 Largura de banda, 149, 400 Ligação de pontos, 151 Linear, 400 Linearidade, 78-81 Lista, 400 Loops, 33 LTI, 400

M MAC (multiply accumulate cell), 401 Magnitude(s), 115, 400 equação, 361 Mallat, Stéphane, 227 Manipulação de números complexos, 125 multiplicação de números complexos, 127 soma de dois números complexos, 125 fasores girantes, 126 números complexos em geral, 126 senoides de mesma frequência, 127 Mantissa, 6 Marca d’água de imagem, 367 MATLAB, 7, 9, 27-67 arredondamento, 54 cálculo de erro, 50 comando help, 28 úteis, 63, 64 como não traçar uma senoide, 44-46 comparação de números com uma tolerância, 52-54 conceitos básicos de programação, 29 continuação de uma linha, 34 declarações condicionais, 32 escalares, 30 intervalos de números, 31 loops, 33 matrizes, 30 saída, 31 vetores, 30 declaração if, 51 while, 51 dicas de programação, 56 escrever comentários, 28 exemplos aritméticos, 34-43 exercícios de programação, 57-63 função ceil, 43, 55

407

fix, 55 floor, 54 round, 54 obter ajuda, 28 operações com variáveis, 27 traçado de uma senoide, 47-50 truncamento, 54 Matriz(es), 30, 400 MPEG (Motion Picture Experts Group), 169 Multiplicação de fasores, 133 de números complexos, 127 de uma função pelo Chapéu Mexicano, 296-299 Multiplicadores, 71 Multirresolução, 257-260, 400

N Normalizado, 400 Notas matemáticas, 363 Número(s), 1 complexo(s), 8, 400 e, 119-121 imaginários, 8 inteiros, 4 negativos, 4, 5 ␲, 112

O Oitava, 400 Operações com senoides, 20 Ortogonal, 400 Ortogonalidade, 254

P Página web, imagens menores em uma, 322 Passa -alta(o)(s) (highpass), 400 filtro, 82 -baixa(o)(s) (lowpass), 400 filtro, 82 -banda (bandpass) filtro, 82 Período, 400 de amostragem, 149 Pi (␲), 112, 355 Pixel, 400 Polo, 400 Ponto fixo, 4, 400 fracionário, 4 Porta, 2 Potências de − z, 215 Precisão, 6 Previsão do tempo, 368 Processamento digital de sinais, 1 em lote, 321 Programação, conceitos básicos de, 29 continuação de uma linha, 34 declarações condicionais, 32 escalares, 30 intervalos de números, 31 loops, 33 matrizes, 30 saída, 31 vetores, 30 Projeto(s) de filtro, 339 filtro wavelet, 253, 254

05/07/12 11:16

408

Índice

métodos de janelamento, 339 FIR, 341 ideias de, 367, 368

Q Quadratura conjugada, filtros em, 232 filtros espelhados em, 232 Quatro coeficientes, filtros com, 253, 254

R Raiz quadrada do erro médio, 362 Rápida de Fourier, transformada, 171, 401 Rastreador de bola de basquete, 368 Reconstrução, 151, 400 perfeita, 231 Reexame da combinação sequencial de filtros com z, 211, 212 Região de convergência, 400 Rejeita-banda (bandstop), filtro, 82 Remoção de olhos vermelhos, 367 Representação de um sinal digital como uma soma de senoides, 136 Resposta ao impulso, 400 de frequência de filtros, 82, 216-222 de magnitude de frequência, 400 em frequência de som, 337-339 para uma wavelet, 281-283 finita ao impulso, 71, 400 infinita ao impulso, 400 Retardo por uma unidade, 213 Reversão da transformada contínua de wavelet, 306-309 Revisão de números complexos, 116-117 Ripples, 141 Ruído, 401 amostragem e o, de alta frequência, 151

S Saída, 31 Semicondutor Complementar de Óxido Metálico, 2 Senoides, 401 amplitudes, 115, 116 circuitos unitários, 113, 114 equação de Euler, forma alternativa da, 123 espectro, 142-144 fórmula de Euler, 117, 122, 123 inversa de Euler, 123-125 geometria e trigonometria, revisão, 111 manipulação de números complexos, 125 multiplicação de fasores, 133 número e, 119-121 ␲, 112 principal valor do deslocamento de fase, 114, 115 representação de um sinal digital como uma soma de senoides, 136 revisão de números complexos, 116, 117

Weeks indice.indd 408

sinais harmônicos, 133-136 soma de fasores girantes, 128-132 traçados de, 21 Separação de objetos, 368 Significando (mantissa), 6 Silício, 2 Símbolos comuns, 357 Simplificação, nível adicional de, 321 Sinal(is), 11-14, 401 analógico, 14 curto, 141 digital, 14-16, 136 em ondas, decomposição de um, 244-253 harmônicos, 133-136, 401 periódico, 141 ruído, relação, 363 senoidais, 21 Síntese, 231 Sistema, 17, 401 binário, 2 de base 10, 1 de identificação de gatos, 368 númerico de base 10, 2 decimal, 1 Sobreamostragem, 150, 239, 401 com Daubechies 4, 242-244 de dois coeficientes, 241 Sobreposição espectral, 152, 401 dobramento, 155 exemplo de, 153 localização de replicações após amostragem, 157-160 Sobressinal, 141 Solução recursiva de um quebracabeça Sudoku, 332-337 Som, aplicações com, 315 Soma(s), 23 de dois números complexos, 125 de fasores girantes, 126, 128-132 de números complexos em geral, 126 de senoides de mesma frequência, 127 Somadores, 71 Somatórios, 71 Subamostragem, 150, 239, 401 com Daubechies 4, 242-244 com matrizes, 274 de dois coeficientes, 241, 242 Sub-banda, codificação de, 399 Subsinal, 141 Substituição de dois filtros FIR em série, 210, 211 Subtrativa, transformada, 19

T Tamanho da palavra, 3, 401 Taxa de Nyquist, 160 Tempo, 23 Tendências de um filtro IIR simples, 222 Teoria da transformada de wavelet, 264-271 do deslocamento para a DFT, 180, 181 Teste de causalidade, 80 de linearidade, 80 Traçado(s) de senoide(s), 21, 47-50 do espectro, 174-179 Tradutor de cães, 368 potências de − z, 215

reexame da combinação sequencial de filtros com z, 211, 212 resposta de frequência de filtros, 216-222 retardo por uma unidade, 213 substituição de dois filtros FIR em série, 210-211 tendências de um filtro IIR simples, 222 Transformação, 18 Transformada, 18, 401 aditiva, 19 contínua de Fourier, 401 equação, 360 de wavelet, 287-314, 401 avaliação de integrais, 292-296 Chapéu Mexicano, 287-289 deslocamento de chapéu, 289-291 e escalonamento, 292 equação, 365 escalonamento de chapéu, 291 exemplos da, 299-301 em uma função quadrado, 303 exemplo analítico em uma função, 301 impulso, 302 multiplicação de uma função pelo Chapéu Mexicano, 296-299 programa para computar, 304 convolução, 305 reversão da transformada contínua de wavelet, 306-309 de Fourier, 169-208, 401 amostragem de frequência e o espectro, 193, 194 cálculo da DFT direta e inversa sob a forma de operações matriciais, 203 programa para o, 204-206 como a transformada z reduz-se à, 214 DFT direta e inversa, 184-187 discreta, 171, 172 inversa, 182-184 no tempo, 171 equação, 360 exibição da DFT como ângulos em gráficos de vetores unitários, 201 funcionamento da DFT, 194 quando a frequência de análise coincide com a real, 195-197 não coincide com a real, 197-199 harmônicos e a transformada de Fourier, 189-193 inserção de amostras nulas (zero padding), 179 rápida de Fourier, 171 teoria do deslocamento para a DFT, 180, 181 traçado do espectro, 174-179 vazamento espectral, 187-189 de Haar, 20, 232 afeta o ângulo de um ponto, 235 o raio de um ponto, 234

05/07/12 11:16

Índice

como a, a uma rotação de 45o, 234 de Laplace, 19, 365 de wavelet, 19 equação, 364 teoria da, 264-271 direta, 231 de Fourier funcionamento da, 194 cálculo da, direta e inversa sob a forma de operações matriciais, 203 programa para o, 204-206 exibição da, como ângulos em gráficos de vetores unitários, 201 quando a frequência de análise coincide com a real, 195-197 não coincide com a real, 197-199 discreta de Fourier, 171, 172, 227-285, 401 banco de filtros de dois canais, 231 bases ortonormais, 254-257 de wavelet, 227- 229, 401 equação, 364 decomposição de um sinal em ondas, 244-253 equação, 361 execução da DWT com matrizes, 271 análise da multiplicação matricial, 271 aplicação de DWT, 272 combinação de passa-baixas e passa-altas em uma matriz, 275 generalização da DWT com matrizes, 277-280

Weeks indice.indd 409

subamostragem com matrizes, 274 filtros com quatro coeficientes, 253, 254 em quadratura conjugada, 232 espelhados em quadratura, 232 inversa, 182-184 equação, 361 multirresolução, 257-260 projeto de filtro wavelet, 253, 254 resposta de magnitude em frequência para uma wavelet, 281-283 sobreamostragem, 239 com Daubechies 4, 242-244 de dois coeficientes, 241 subamostragem, 239 com Daubechies 4, 242-244 de dois coeficientes, 241, 242 teoria da transformada de wavelet, 264-271 wavelet(s) biortogonais, 260-264 de quatro coeficientes de Daubechies, 236-239 inversa, 231 rápida de Fourier, 171, 401 subtrativa, 19 z, 209-225, 401 como reduz-se à transformada de Fourier, 214 demonstração da equivalência da convolução no domínio do tempo em relação à multiplicação no domínio da frequência, 215, 216 equação, 365 Transistor, 2 Truncamento, 54

409

U Unidade(s) de multiplicação e acumulação, 81, 401 de retardo, 71

V Valor do deslocamento de fase, 114, 115 Variância da amostra, 362 de uma variável aleatória, 362 Variáveis, 355, 356 Vazamento DFT, 401 espectral, 187-189 Very Large Scale Integration. Veja Integração em escala muito grande Vetor(es), 30, 401 de fase, 117

W Wavelet, 401 biortogonais, 260-264 Chapéu Mexicano, 287-289 de quatro coeficientes de Daubechies, 236-239 transformada de, 19 equação, 364 teoria da, 264-271

Z z, transformada, 401 equação, 365 Zero, 1, 401

05/07/12 11:16