Site menu Modem QAM atinge 16800bps
e-mail icon
Site menu

Modem QAM atinge 16800bps

e-mail icon

Como dito no artigo anterior, estava pensando a respeito de técnicas no domínio do tempo, para interpretar o sinal QAM no lado RX, de modo a permitir taxas de baud comparáveis com a freqüência da portadora. Não planejava colocar isso em prática, mas não pude resistir.

A "coisa" veio a funcionar mais cedo que o esperado, e a performance foi além das minhas expectativas. O modem consegue receber a 2400baud com uma portadora de 1800Hz, tal qual modems mais rápidos do mundo real fazem. No momento ele consegue lidar com símbolos de até 7 bits cada, o que se traduz num bitrate de 16800bps. Ir além disso exige mais melhorias na detecção de símbolos, que nesta versão é reconhecidamente sub-ótima.

O modem V3 é basicamente igual ao V2, exceto pela decodificação QAM para símbolos complexos. A versão V2 utilizava uma abordagem "clássica" (demodulação + filtro FIR). A versão V3 analisa diretamente o sinal QAM. Assim sendo, vou listar apenas a parte relevante do RX V3:

# Proof that we don't need exact carrier frequency
CARRIER += random.random() * 20 - 10
# Proof that we don't need exact carrier phase
ra = random.random() * 2 * math.pi

symbols = []
f = CARRIER * 2 * math.pi / SAMPLE_RATE
carrier_90 = SAMPLE_RATE / CARRIER / 4.0
carrier_90 = int(carrier_90)
phase_error = 2 * math.pi * CARRIER / SAMPLE_RATE / 2.0
recovered = []

for t in range(0, len(qam) - carrier_90 - 2):
    # Take derivatives of now and 90 degrees in future
    d = (qam[t+1] - qam[t]) / f
    d90 = (qam[t + carrier_90 + 1] - qam[t + carrier_90]) / f

    st = math.sin(f * t + ra)
    ct = math.cos(f * t + ra)

    a = d90 * ct + d * st
    b = d90 * st - d * ct

    try:
        tphase = -b / a
        phase = math.atan(tphase)
    except ZeroDivisionError:
        phase = math.pi / 2
        if (-b * a) < 0:
            phase = -phase

    if (abs(d) > abs(d90)):
        amplitude = -d / (st * math.cos(phase) + ct * math.sin(phase))
    else:
        amplitude = -d90 /
           (-st * math.sin(phase) + ct * math.cos(phase))

    if amplitude < 0:
        amplitude = -amplitude
        phase += math.pi

    phase += math.pi * 2
    phase %= math.pi * 2

    cpx = crect(amplitude, phase)
    # print t, int(phase * 180 / math.pi) % 360, cpx

    recovered.append(cpx)

Essa técnica é baseada no fato do sinal QAM ser gerado por uma fórmula muito simples:

s(t) = m . cos(2πft + p)

onde "m" e "p" são as coordenadas polares do símbolo, dentro da constelação. A derivada dessa fórmula é outra fórmula simples:

s'(t) = 2πf.m.sin(2πft + p)

O problema da decodificação QAM é: dado s(t) e/ou s'(t) — cujos valores são derivados das amostras recebidas — encontre as incógnitas "m" e "p". Precisamos de duas amostras s'(t), que estejam perto o suficiente para pertencer ao mesmo símbolo, de modo a montar um sistema de equações e resolver as incógnitas.

É exatamente o que faz o código Python listado mais acima. As amostras são coletadas com 90 graus de fase de distância, porque então as equações simplificam muito bem.

Escolhi a equação de s'(t) em vez de s(t) porque o sinal QAM pode não estar centrado em zero. Isto é, ele pode ter um "DC bias", um valor constante positivo ou negativo. Já a diferença entre amostras adjacentes está livre desse "bias". A diferença entre amostras discretas é equivalente ao conceito de derivada de um sinal contínuo.

É importante medir os valores de s'(t) com distância exata de 90 graus de fase. Essa exatidão só é possível se 90 graus de fase equivalem a um número inteiro de amostras digitais. Isto implica que a taxa de amostragem WAV seja um múltiplo de 1800 / 4 = 450Hz.

Utilizar um arquivo WAV de 43200Hz funciona perfeitamente (450x96=43200), enquanto um WAV de 45000Hz, que é melhor na teoria e é múltiplo da portadora, não consegue transmitir mais que 4 bits por símbolo. Um WAV de 44100Hz tem desempenho ainda pior: 3 bits por símbolo.

Talvez eu tenha "trapaceado" um pouco ao escolher uma taxa de amostragem "perfeita" para o arquivo WAV, mas suspeito que um modem do mundo real faz exatamente o mesmo, usando uma taxa de amostragem adequada ao protocolo em uso. De qualquer forma, essa técnica que inventei meio ad-hoc não tenciona ser a melhor do mundo, é apenas um exercício para mostrar que a recepção QAM pode ser executada puramente no domínio do tempo, sem um demodulador clássico.

e-mail icon