Rádio é algo com que eu gostaria de ter brincado mais. Por diversas razões, esse interesse está sempre enterrado lá no fundo das coisas por fazer um dia, e pelo andar da carruagem talvez nunca chegue a fazê-lo. Vou me consolando com código Morse e ouvindo ondas curtas, e NDBs aeronáuticos.
Para um sinal, uma informação útil, viajar sobre ondas de rádio, ela precisa ser modulada. Modular é transpor o sinal para uma banda de freqüência e formato compatíveis com o meio de transmissão. As técnicas de modulação são unha e carne com telecomunicações e teoria da informação, e é possível brincar com modulação, ou pelo menos simulá-la, num computador. Não sou um engenheiro de telecomunicações, mas o tema me interessa assim mesmo. A simplicidade do processo faz parecer "milagre".
Neste artigo, vou ilustrar as técnicas de AM — modulação de amplitude ou, como preferem os locutores de rádio da velha guarda, amplitude modulada. Nosso meio de transmissão é o arquivo de áudio WAV. Para começar, precisamos de um sinal de banda-base, isto é, algum áudio que preencha toda a banda desde 0Hz. Gravei minha própria (e desagradável) voz para este fim:
A largura de banda ou espectro de freqüências deste áudio é:
A parte amarela do espectro é a mais relevante, já que o eixo Y é logarítmico (dB). Um sinal 30 decibels mais fraco é 1000 vezes mais fraco. O espectro acima de 5000Hz é basicamente "ruído digital", não é a voz. Nas próximas figuras, vou cortar fora a parte baixa do gráfico. (Se você for curioso, ou não confia nos meus gráficos, você pode fazer o download dos arquivos WAV e analisar o espectro no seu aplicativo predileto.)
A modulação AM exige que o sinal seja restrito em largura de banda, de modo que o sinal modulado também "caiba" numa faixa de freqüência de rádio. Assim, precisamos aplicar um filtro passa-baixas no meu arquivo de voz, e definimos a freqüência de corte em 1000Hz. O resultado é a mesma voz, porém com menos qualidade.
O espectro deste sinal filtrado é o seguinte:
Fiz uso do plug-in de filtro passa-baixas do Audacity, que não é perfeito mas é suficiente para ilustrar nossa modulação. Em outros artigos, implementarei um filtro do zero, em Python.
Modulação de amplitude é matematicamente muito simples: basta multiplicar o sinal original por uma onda senoidal contínua, conhecida como portadora. No nosso caso, escolhi uma portadora de 3000Hz, bem acima do corte do sinal original que foi 1000Hz. O ruído da portadora pura parece um assobio:
O resultado da modulação (voz multiplicada pela portadora) é:
Note que é quase impossível entender a palavra falada no áudio modulado. Para ser preciso, este tipo de modulação é denominado AM-SC (AM-Suppressed Carrier, ou Modulação de Amplitude com Portadora Suprimida), por uma razão que vou esclarecer mais adiante.
A modulação pega minha voz, cuja banda era 0-1000Hz, e move para 2000-4000Hz. Além de deslocada, a banda dobrou, porque a modulação AM cria realmente duas "cópias" do sinal original: uma na banda 2000-3000Hz e outra na banda 3000-4000Hz. Assim sendo, a modulação AM "desperdiça" banda porque o sinal modulado ocupa duas vezes mais banda:
Ainda assim, a modulação AM é muito útil, porque ela permite "mover" o sinal sonoro para freqüências que se prestam à transmissão eletromagnética (bastaria escolher uma portadora na faixa de 1MHz). Mais importante, a modulação permite que múltiplos sinais sejam alocados e transmitidos num mesmo meio de transmissão (no caso do rádio, é o "ar", compartilhado por todos os rádios), cada um ocupando sua banda, e cada um pode ser recuperado separadamente.
Como disse, a modulação acima foi AM-SC, que não é utilizada pelas radiodifusoras. A modulação AM para radiodifusão tem o seguinte som:
Nesta modalidade de AM, podemos ouvir a portadora, que não podíamos em AM-SC. O espectro AM de radiodifusão é semelhante ao AM-SC porém possui um "pico" exatamente em cima da freqüência da portadora:
O seguinte programa escrito em Python gerou as modulações AM e AM-SC:
#!/usr/bin/env python
import wave, struct, math
baseband = wave.open("paralelepipedo_lopass.wav", "r")
amsc = wave.open("amsc.wav", "w")
am = wave.open("am.wav", "w")
carrier = wave.open("carrier3000.wav", "w")
for f in [am, amsc, carrier]:
f.setnchannels(1)
f.setsampwidth(2)
f.setframerate(44100)
for n in range(0, baseband.getnframes()):
base = struct.unpack('h', baseband.readframes(1))[0] / 32768.0
carrier_sample = math.cos(3000.0 * (n / 44100.0) * math.pi * 2)
signal_am = signal_amsc = base * carrier_sample
signal_am += carrier_sample
signal_am /= 2
amsc.writeframes(struct.pack('h', signal_amsc * 32767))
am.writeframes(struct.pack('h', signal_am * 32767))
carrier.writeframes(struct.pack('h', carrier_sample * 32767))
O modulador AM-SC multiplica o sinal original (devidamente limitado em banda por algum filtro) pela portadora, que é uma simples onda senoidal.
A modulação AM de radiodifusão é igual a AM-SC, mas a portadora é adicionada ao resultado final. Isto permite que o receptor AM seja mais simples e barato, conforme veremos.
Muito bem, é hora de provar que podemos recuperar a voz original a partir dos áudios modulados. O demodulador AM-SC é tão simples quanto o modulador: o sinal é novamente multiplicado pela mesma portadora — técnica denominada "detecção por produto". O resultado é passado por um filtro passa-baixas.
Estes são os áudios demodulados, primeiro sem a filtragem final:
E com a filtragem final:
Para quem prefere ver a modulação acontecendo no domínio do tempo, segue as formas de onda da modulação AM-SC. Azul é o sinal original, vermelho é a portadora, roxo é o sinal modulado, verde é o sinal demodulado no receptor.
Note que o sinal modulado AM-SC tem um "envoltório" ou "envelope", ou seja, os picos de onda guardam uma relação com o sinal original, como se fossem envolvidos por ele.
A demodulação AM-SC é igual à modulação, exceto pela filtragem final compulsória. Multiplicar um sinal duas vezes pela mesma portadora transfere o áudio de volta para a sua banda original (0-1000Hz) e cria uma nova cópia em torno de 6000Hz, que deve ser filtrada.
Este é o código do demodulador AM-SC:
modulated = wave.open("amsc.wav", "r")
demod_amsc_ok = wave.open("demod_amsc_ok.wav", "w")
demod_amsc_nok = wave.open("demod_amsc_nok.wav", "w")
demod_amsc_nok2 = wave.open("demod_amsc_nok2.wav", "w")
for f in [demod_amsc_ok, demod_amsc_nok, demod_amsc_nok2]:
f.setnchannels(1)
f.setsampwidth(2)
f.setframerate(44100)
for n in range(0, modulated.getnframes()):
signal = struct.unpack('h', modulated.readframes(1))[0] / 32768.0
carrier = math.cos(3000.0 * (n / 44100.0) * math.pi * 2)
carrier_phased = math.sin(3000.0 * (n / 44100.0) * math.pi * 2)
carrier_freq = math.cos(3100.0 * (n / 44100.0) * math.pi * 2)
base = signal * carrier
base_nok = signal * carrier_phased
base_nok2 = signal * carrier_freq
demod_amsc_ok.writeframes(struct.pack('h', base * 32767))
demod_amsc_nok.writeframes(struct.pack('h', base_nok * 32767))
demod_amsc_nok2.writeframes(struct.pack('h', base_nok2 * 32767))
A nova cópia do sinal em 6000Hz é indesejada, pode ser considerada ruído, e confunde alguns leitores. Eles simulam a demodulação numa planilha Excel e não entendem de onde vem esse componente de alta freqüência.
A figura a seguir mostra o espectro do áudio AM-SC demodulado, antes da filtragem passa-baixas. A parte amarela é a "parte boa":
É interessante usar um pouco de matemática para esclarecer este ponto. Isto aplica-se a AM e também a QAM. O sinal original de áudio, representado pela função f(t), é modulado (multiplicado) por uma cossenóide (a portadora). Assim, o sinal transmitido é f(t).cos(c.t).
A demodulação transforma novamente o sinal para f(t).cos(c.t).cos(c.t). Se relembramos das identidades trigonométricas do colegial, podemos converter a última forma para f(t).[1+cos(2.c.t)] e finalmente para f(t)+[f(t).cos(2.c.t)].
Agora está claro (se você manja de fórmulas) que o sinal demodulado é uma soma de duas partes: o áudio f(t) original (que nós queremos) e o mesmo áudio modulado por uma portadora de 6000Hz (que é indesejável).
Como a freqüência da portadora em aplicações reais é muito mais alta que a largura de banda do sinal base, a parte indesejável cai numa freqüência tão alta que é fácil livrar-se dela usando um filtro passa-baixas extremamente simples. E mesmo que não fosse removida, ninguém ouviria.
Um pequeno grande problema da demodulação AM-SC: a onda da portadora gerada no lado receptor precisa ser exatamente igual à do transmissor, tanto em freqüência quanto em fase. Qualquer diferença destrói a demodulação.
No código Python exibido antes, simulei dois problemas de demodulação: portadora com fase errada (90 graus atrasada) e com freqüência errada (100Hz acima). Os resultados são muito ruins conforme você pode ouvir abaixo (já filtrados). A portadora fora de fase simplesmente não encontra o áudio original; freqüência errada distorce a ponto de tornar incompreensível:
A bem da verdade, uma portadora com desvio menor que 90 graus ainda detectaria o sinal, embora com volume menor (quanto menor o desvio, maior o volume). Assim sendo, podemos usar um demodulador de quadratura (I/Q) que usa duas portadoras defasadas de 90 graus. A combinação dos sinais detectados será sempre o sinal com volume total. Mas a frequência dessas portadoras ainda precisa ser exata.
A modulação AM-SC não fornece uma referência para sincronizar a portadora, o que torna difícil gerar a portadora com as qualidades necessárias. Talvez por conta disso, AM-SC é pouco empregado na prática (*) enquanto AM é amplamente usado.
(*) Um uso supreendente de AM-SC é na transmissão de FM stéreo. O segundo canal de áudio é codificado dentro do próprio canal de áudio, com uma portadora de 38KHz. O problema do sincronismo da portadora é resolvido enviando um sinal-piloto de 19kHz.
A propósito, vamos ver como funciona um receptor AM baseado na técnica de "detecção de envelope":
modulated = wave.open("am.wav", "r")
f = demod_am = wave.open("demod_am.wav", "w")
f.setnchannels(1)
f.setsampwidth(2)
f.setframerate(44100)
for n in range(0, modulated.getnframes()):
signal = struct.unpack('h', modulated.readframes(1))[0] / 32768.0
signal = abs(signal)
demod_am.writeframes(struct.pack('h', signal * 32767))
Aprecie quão simples ele é; note a ausência de funções trigonométricas. AM de radiodifusão pode ser detectado por um simples diodo, que corta fora a parte negativa da onda. O diodo ainda pode ser improvisado a partir de um cristal de galena ou uma gilete enferrujada.
Faltou conferir o resultado auditivo do detector de envelope:
Depois da filtragem final:
Há um ruído residual causado pelo detector abs(), pois ele cria picos no sinal demodulado. Num rádio AM real, esses picos cairiam numa freqüência muito alta, inaudível e fácil de remover. (Lembrar sempre que estamos usando uma portadora de freqüência relativamente baixa, 3000Hz.)
Formas de onda (domínio do tempo) da modulação AM:
O sinal modulado AM mostra um efeito de envelope mais pronunciado que AM-SC: os picos de onda seguem claramente o contorno do sinal original. Isto torna a demodulação muito fácil, pode acontecer até mesmo acidentalmente, através de uma obturação dentária ou encostando uma folha numa antena de transmissão.
A planilha que gerou as formas de onda acima pode ser abertaaqui.
Um receptor AM não é obrigado a usar a detecção de envelope. Ele pode usar um demodulador "de verdade", tal qual AM-SC. É fácil gerar a portadora no receptor, porque AM transmite a mesma junto com o sinal "útil". Tal receptor tem vantagens em relação ao detector simples: maior resistencia ao ruído e compatibilidade com sinais AM-SC/AM-SSB.
A modulação AM — AM-SC adicionado da portadora — é matematicamente equivalente a adicionar um grande "DC bias" ou valor positivo fixo ao sinal original, e então modular como AM-SC. Isto garante que a portadora seja sempre multiplicada por um valor positivo, e o sinal modulado está sempre em fase com a portadora. A fase do sinal AM não carrega nenhuma informação, e o receptor pode desprezá-la.
Já na modulação AM-SC, o conteúdo original é modulado "como está". Nos momentos em que o sinal de áudio tem polaridade negativa, a saída do transmissor tem polaridade oposta à portadora. Assim sendo, a fase do sinal AM-SC possui uma informação que não pode ser desprezada: a polaridade do sinal.
Se você tentar receber AM-SC com um detector de diodo, a fase e portanto a polaridade é perdida. O resultado será muito distorcido, embora possa fazer lembrar o áudio original. Se era voz humana, soará como o Pato Donald, porque a demodulação incorreta dobra as freqüências e adiciona harmônicas.
AM-SC tem algumas vantagens frente ao AM. Pode transmitir mais informação usando menos banda, e não despende energia enviando a portadora. Outro problema do AM é a forma que ele lida com freqüências muito baixas presentes no conteúdo original, como os graves de subwoofer de uma música. O "DC bias" tem de ser removido em algum ponto da demodulação, e o conteúdo de baixa freqüência será perdido na mesma leva. É um problema sério para transmissão de vídeo, que é mais bem servido por alguma variante de AM-SC.
Ambas as modulações AM e AM-SC "desperdiçam" banda, porque enviam duas "cópias" do mesmo sinal, abaixo e acima da portadora. Podemos eliminar uma delas, transmitindo apenas a outra. Esta modalidade é chamada AM-SSB (Suppressed sideband - banda lateral suprimida).
O espectro de modulação é obviamente diferente de AM-SC. A banda lateral abaixo de 3000Hz está praticamente extinta:
As formas de onda da modulação SSB podem ser vistas abaixo:
Na figura acima, temos dois elementos novos: uma versão 'Hilbert' do sinal original, em vermelho (90 graus adiantado) e uma segunda portadora 'Carrier Q' em amarelo, também adiantada. Você pode ignorar estes itens por ora; eles farão sentido mais adiante, quando falarmos da transformada de Hilbert.
Bem diferente de AM e AM-SC, o sinal modulado SSB não apresenta um envelope, apesar de ainda ser proporcional à amplitude do sinal original (0.5). Como o sinal original é uma senóide "pura", a onda modulada também é "pura", com freqüência muito próxima à portadora. Isto é congruente com o fato da modulação SSB ser uma translação perfeita do sinal original para outra banda, sem gerar cópias adicionais nem distorções.
O áudio a seguir ilustra como soa uma modulação AM-SSB (banda inferior removida, banda superior mantida):
Não é muito diferente de AM-SC, e de fato o demodulador AM-SC é capaz de processar o sinal AM-SSB sem qualquer modificação:
Ops, faltou a filtragem passa-baixas depois da demodulação:
Transmitindo apenas uma cópia do conteúdo original, e não tendo de transmitir a portadora, o sinal AM-SSB é econômico tanto em banda quanto em energia. Ele tem uma segunda grande vantagem sobre AM-SC. Veja o que acontece quando demodulamos um sinal AM-SSB com portadora local fora de fase:
A voz foi recuperada perfeitamente. Assim, fica demonstrado que um receptor AM-SSB não precisa se preocupar com a fase da portadora. Apenas a freqüência é importante, e esta tem de ser muito bem controlada: qualquer desvio causa uma distorção semelhante à que constatamos em AM-SC:
AM-SSB é a modulação padrão em radioamador de longa distância, TV analógica, etc. E a mesma técnica é empregada em multiplexadores FDM e incontáveis outros dispositivos que precisam enviar muitos sinais através de um único canal.
AM-SSB, assim como AM, tem um problema com freqüências muito baixas no sinal original. A TV analógica precisa delas, então emprega a modulação VSB (Vestigial Sideband - banda lateral vestigial). Em VSB, nem toda a banda lateral "excedente" é suprimida; fica o suficiente para que o conteúdo de baixa freqüência seja recebido como AM-SC, com transição para AM-SSB em freqüências mais altas.
Finalmemente, há uma questão de implementação do AM-SSB. É possível remover a banda lateral "excedente" com um filtro analógico, porém tal filtro é difícil de construir (e portanto caro). Mas podemos gerar um sinal SSB sem filtro de saída.
Uma técnica interessante, empregada por rádios com capacidade de processamento digital de sinais (DSP), é a transformada de Hilbert. Essa transformada muda a fase do áudio em 90 graus. Não é um simples atraso fixo; é uma mudança de fase sobre cada freqüência presente no áudio.
O áudio transformado parece exatamente igual ao original, porque nossos ouvidos não percebem diferenças de fase. Porém, graças às identidades trigonométricas, modular o sinal transformado com uma portadora defasada, e adicionar o resultado a um modulador AM-SC, cancela a "cópia" inferior, deixando apenas a banda superior. Obtemos assim um sinal AM-SSB perfeito sem qualquer filtragem.
A transformada de Hilbert é mais facilmente descrita e implementada no domínio da freqüência (usando FFT), mas também pode ser implementada como um filtro FIR (convolução no domínio do tempo). Tal filtro seria "não-causal": ele depende dos valores futuros do sinal para calcular uma saída no presente.
Isso parece absurdo, mas na prática se resolve colocando o filtro para trabalhar no "futuro do pretérito". O único problema é que a saída do filtro tem um certo atraso em relação à entrada — na nossa implementação, 1,13 milissegundo.
O seguinte programa Python implementa o modulador AM-SSB baseado em transformada de Hilbert, este último implementado como um filtro FIR.
#!/usr/bin/env python
import wave, struct, math, numpy
hlen = 500.0
hilbert_impulse = [ (1.0 / (math.pi * t / hlen)) \
for t in range(int(-hlen), 0) ]
hilbert_impulse.extend([0])
hilbert_impulse.extend( [ (1.0 / (math.pi * t / hlen)) \
for t in range(1, int(hlen+1)) ] )
hilbert_impulse = [ x * 1.0 / hlen for x in hilbert_impulse ]
baseband = wave.open("amssb_hilbert_orig.wav", "r")
amssbh = wave.open("amssb_hilbert.wav", "w")
for f in [amssbh]:
f.setnchannels(1)
f.setsampwidth(2)
f.setframerate(44100)
base = []
base.extend([ struct.unpack('h',
baseband.readframes(1))[0] / 32768.0 \
for n in range(0, baseband.getnframes()) ])
base.extend([ 0 for n in range(0, int(hlen)) ])
base_hilbert = numpy.convolve(base[:], hilbert_impulse)
for n in range(0, len(base) - int(hlen)):
carrier_cos = math.cos(3000.0 * (n / 44100.0) * math.pi * 2)
carrier_sin = -math.sin(3000.0 * (n / 44100.0) * math.pi * 2)
s1 = base[n] * carrier_cos
# time-shifted signal because Hilbert transform is noncausal
s2 = base_hilbert[n + int(hlen)] * carrier_sin
signal_amssbh = s1 + s2
amssbh.writeframes(struct.pack('h', signal_amssbh * 32767))
A análise de espectro mostra que, com esta técnica, a banda inferior foi removida muito melhor do que com filtragem:
Produzir um sinal AM-SSB como esse, com duas portadoras defasadas em 90 graus, sugere que AM-SSB é na verdade uma forma especial de QAM analógico (QAM - Quadrature Amplitude Mmodulation ou Modulação por Amplitude e Quadratura). A modulação QAM permite transmitir dois sinais ao mesmo tempo, e de fato nós estamos enviando dois sinais via modulador AM-SSB Hilbert: o áudio original e o áudio transformado.
Ambos os sinais são interdependentes e equivalentes em conteúdo de informação. Então nós estamos, num certo sentido, enviando duas cópias da mesma coisa — muito embora a intenção seja a remoção de uma banda lateral, e não criar redundância.
Encarar AM-SSB como se fosse QAM também explica porque o demodulador AM-SSB consegue detectar o áudio original com uma portadora fora de fase. Uma vez que o mesmo áudio está sendo enviado em duplicata, modulado por duas portadoras ortogonais, o receptor sempre consegue demodular ou uma ou outra cópia, ou mais provavelmente uma mistura das duas.
As técnicas de modulação vistas acima podem ser usadas para fins mais lúdicos, como efeitos sonoros. No exemplo abaixo, usamos o modulador para deslocar o "pitch" sonoro em apenas 100Hz, dando um efeito meio onírico meio malaise:
Note que o áudio ficou meio desafinado, porque deslocamos o espectro de forma linear, então as harmônicas musicais que normalmente são múltiplas inteiras da fundamental, deixaram de ser e.g. fundamental de 200Hz e harmônica de 400Hz (2:1), passaram a ser 300Hz e 500Hz, respectivamente (5:3).
Esse efeito não serve para consertar uma voz desafinada ou mudar o pitch de uma música sem alterar sua duração e mantendo-a afinada. Para isso, um algoritmo bem mais complexo denominado phase vocoder é quem pode fazer o serviço.
Para provar que o deslocamento de freqüência realizado pelo transformador de Hilbert é reversível, mesmo quando o deslocamento é relativamente pequeno, segue o áudio acima transladado na direção aposta:
Fora alguns tiques, que devem ser resultado de "clipping" (tive preguiça de refinar os scripts e resolver esta falha), o áudio produzido pela transformação contrária é igual ao original. Acho que está suficiente para provar o ponto.
Os scripts que geraram os áudios acima estão aqui e aqui, respectivamente.