Faça um modem (software)!


14

Objetivo

Projetar um mo dulator / dem par odulator aos dados transmitem com precisão o mais rápido possível sobre simulado o serviço telefônico comum (POTS) .

Passos

  1. Gere alguns dados aleatórios ( /dev/randomou similares) que levarão de 3 a 4 segundos para transmitir
  2. Module os dados com seu modulador para produzir um arquivo de áudio
  3. Passe o arquivo de áudio pelo simulador de POTS . Se você não possui Python / Scipy, pode fazer upload de um arquivo com o formulário ou fazer uma solicitação de API JSON.
  4. Desmodule o arquivo de áudio de volta aos dados binários
  5. Valide se a entrada e a saída são iguais * (o limite de 1 em cada 1000 bits pode ser corrompido)
  6. Pontuação é o número de bits transmitidos dividido pelo tamanho do arquivo de áudio (bits / segundo)

Regras

  • O arquivo de entrada deve ter 3-4 segundos, 44,1 kHz, mono.
  • Execute o simulador com um SNR de 30 dB (é padrão)
  • O desmodulador deve reconstruir os dados transmitidos com uma taxa de erro de bits não superior a 10 -3 (1 por mil bits).
  • Nenhuma compactação digital é permitida (ou seja, compactar os dados. Está fora do escopo do desafio.)
  • Nenhuma tentativa de inserir dados em frequências acima de 4 kHz. (Meus filtros não são perfeitos, mas são razoavelmente semelhantes a POTS com um número relativamente pequeno de toques.)
  • Se o seu protocolo de modem exigir um preâmbulo curto (não mais que 1 segundo) para sincronizar / calibrar o receptor, ele não será penalizado.
  • Se possível, hospede o arquivo de áudio em algum lugar acessível para que possamos ouvir uma cacofonia de bipes e boops.

Exemplo

Aqui está um exemplo de caderno que demonstra a modulação / desmodulação com um simples "on-off keying" (amostras de áudio incluídas!).

Marcaria 100 (bits / segundo). Observe que está transmitindo com um SNR de 5 dB muito pior.


2
Isso é diferente de um desafio comum de "comprimir esses dados binários"? Em caso afirmativo, você poderia esclarecer quão exatamente isso difere?
Maçaneta

1
Aqui você está modulando dados (transformando-os em algo analógico) e depois o contrário. Pode-se talvez chamá-lo de "compressão analógica"
Nick T

Desculpe, não sei ao certo como esse desafio funciona. A palavra "modular" nem aparece no artigo da Wikipedia que você vinculou. Você poderia incluir mais informações básicas ou esclarecer as especificações?
Maçaneta

4
wget wikipedia.org/Special:Random | grep title | texttospeech audio.wav speechtotext POTSaudio.wav | wget wikipedia/wiki/$text
TessellatingHeckler

1
Este é um desafio incrível, tentarei encontrar tempo para enviar uma resposta!
GoatInTheMachine

Respostas:


7

MATLAB, 1960 bps

Aqui está minha tentativa atualizada:

fs = 44100; %44.1kHz audio rate
fc = 2450;  %2.45kHz carrier - nice fraction of fs!
fsym = fc/5; %symbol rate

tmax = 4; %about 4 seconds worth

preamblesyms = 6;

t = 1/fs:1/fs:(tmax+preamblesyms/fsym);

symbols = preamblesyms+fsym*tmax;
symbollength = length(t)/symbols;
bits = symbols*3;
bitstream = [zeros(1,preamblesyms*3),rand(1,bits-preamblesyms*3)>0.5]; %Add a little preamble of 18 bits
data = bin2dec(char(reshape(bitstream,3,symbols)'+'0'))';

greycode = [0 1 3 2 6 7 5 4];

%Encode the symbols using QAM8 - we use effectively grey code so that
%adjacent symbols in the constellation have only one bit difference
%(minimises error rate)
encoded = zeros(2,symbols);
encoded(1,data==1) = 1/sqrt(2);
encoded(1,data==3) = 1;
encoded(1,data==2) = 1/sqrt(2);
encoded(1,data==7) = -1/sqrt(2);
encoded(1,data==5) = -1;
encoded(1,data==4) = -1/sqrt(2);
encoded(2,data==0) = 1;
encoded(2,data==1) = 1/sqrt(2);
encoded(2,data==2) = -1/sqrt(2);
encoded(2,data==6) = -1;
encoded(2,data==7) = -1/sqrt(2);
encoded(2,data==4) = 1/sqrt(2);

%Modulate onto carrier
carrier = [sin(2*pi*fc*t);cos(2*pi*fc*t)];
signal = reshape(repmat(encoded(1,:)',1,symbollength)',1,[]);
signal(2,:) = reshape(repmat(encoded(2,:)',1,symbollength)',1,[]);
modulated = sum(signal.*carrier)';

%Write out an audio file
audiowrite('audio.wav',modulated,fs);

%Wait for the user to run through the POTS simulator
input('');

%Read in the filtered data
filtered=audioread('audio.pots-filtered.wav')';

%Recover the two carrier signals
preamblecos = filtered(symbollength+1:symbollength*2);
preamblesin = filtered(symbollength+1+round(symbollength*3/4):symbollength*2+round(symbollength*3/4));

%Replicated the recovered carriers for all symbols
carrierfiltered = [repmat(preamblesin,1,symbols);repmat(preamblecos,1,symbols)];

%Generate a demodulation filter (pass up to 0.66*fc, stop at 1.33*fc
%(really we just need to kill everything around 2*fc where the alias ends up)
d=fdesign.lowpass('Fp,Fst,Ap,Ast',0.05,0.1,0.5,60);
Hd = design(d,'equiripple');

%Demodulate the incoming stream
demodulated = carrierfiltered .* [filtered;filtered];
demodulated(1,:)=filtfilt(Hd.Numerator,1,demodulated(1,:));
demodulated(2,:)=filtfilt(Hd.Numerator,1,demodulated(2,:));

%Split signal up into bit periods
recovereddemodulated=[];
recovereddemodulated(1,:,:) = reshape(demodulated(1,:),symbollength,symbols);
recovereddemodulated(2,:,:) = reshape(demodulated(2,:),symbollength,symbols);

%Extract the average level for each bit period. Only look at the second
%half to account for slow rise times in the signal due to filtering
recoveredsignal=mean(recovereddemodulated(1,round(symbollength/2):symbollength,:));
recoveredsignal(2,:)=mean(recovereddemodulated(2,round(symbollength/2):symbollength,:));

%Convert the recovered signal into a complex number.
recoveredsignal=recoveredsignal(2,:) + 1j*recoveredsignal(1,:);

%Determine the magnitude and angle of the symbol. The phase is normalised
%to pi/4 as that is the angle between the symbols. Rounding this to the
%nearest integer will tell us which of the 8 phases it is closest to
recoveredphase = round(angle(recoveredsignal)/(pi/4));
recoveredphase = mod(recoveredphase+8,8)+1; %Remap to an index in the grey code vector.

%Determine the symbol in the QAM8 constellation
recoveredencoded=greycode(recoveredphase);
recoveredencoded(1:preamblesyms)=0; %Assume the preamble is correct for comparison

%Turn it back in to a bit stream
bitstreamRecovered = reshape(dec2bin(recoveredencoded)'-'0',1,[]);

%And check if they are all correct...
if(all(bitstream==bitstreamRecovered))
    disp(['Woop, ' num2str(fsym*4) 'bps']);
else
    error('Its corrupt Jim.');
end

Desde a minha primeira tentativa, eu brinquei um pouco. Agora existe um pequeno preâmbulo no início (períodos de 18 bits, mas pode ser mais curto) que contém apenas uma onda cosseno. Eu extraí isso e o repliquei para criar portadores de seno e cosseno em fases corretamente para desmodulação - como é um preâmbulo muito curto, não contei na taxa de bits conforme suas instruções.

Também desde a primeira tentativa, agora estou usando uma constelação QAM8 para obter 3 bits por símbolo em vez de 2. Isso efetivamente duplica a taxa de transferência. Então, com uma operadora de ~ 2.4kHz, estou atingindo 1960bps.

Também aprimorei a detecção de símbolos para que a média não seja afetada pelos tempos de subida lentos causados ​​pela filtragem - basicamente apenas a segunda metade de cada período de bit é calculada para remover o impacto dos tempos de subida.

Ainda longe da largura de banda do canal teórico de 40kbps da teoria de Shannon-Hartley (assumindo o SNR de 30dB)

Apenas para quem gosta de sons horríveis , esta é a nova entrada:


E caso alguém esteja interessado, esta é a entrada anterior de 960bps


A pontuação é apenas a taxa de transferência, portanto, mantenha seu código claro. Eu adicionei uma sugestão para hospedar seu arquivo de áudio em algum lugar, se é fácil para funsies: D
Nick T

Vou carregar o áudio no meu site. Parece bastante misterioso!
Tom Carpenter

Arquivo de áudio @NickT carregado - veja o link na parte inferior da postagem.
Tom Carpenter

Se você possui uma conta no SoundCloud, pode enviar seu áudio e postar um link, que será reproduzido em sua postagem. ( Exemplo )
Calvin's Hobbies

@NickT thanks. Criei uma conta no soundcloud e a enviei. Eu também fez uma versão atualizada com o dobro da taxa de dados :)
Tom Carpenter
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.