Сделайте (программный) модем!

14

Задача

Дизайн мо dulator / дем odulator пара точно передавать данные как можно быстрее через моделируются обычная старая телефонная связь (POTS) .

меры

  1. Создайте несколько случайных ( /dev/randomили аналогичных) данных, для передачи которых потребуется 3-4 секунды.
  2. Модулируйте данные с помощью вашего модулятора для создания аудиофайла.
  3. Передайте аудиофайл через симулятор POTS . Если у вас нет Python / Scipy, вы можете загрузить файл с формой или сделать запрос JSON API.
  4. Демодулируйте аудиофайл обратно в двоичные данные
  5. Подтвердите, что вход и выход равны ish * (ограничение 1 из каждых 1000 битов может быть повреждено)
  6. Оценка - это количество переданных битов, деленное на длину аудиофайла (бит / с)

правила

  • Входной файл должен быть 3-4 секунды, 44,1 кГц, моно.
  • Запустите симулятор с SNR 30 дБ (по умолчанию)
  • Демодулятор должен восстанавливать передаваемые данные с частотой ошибок по битам не более 10 -3 (1 на тысячу битов).
  • Цифровое сжатие не допускается (т.е. архивирование данных. Это выходит за рамки задачи).
  • Не пытайтесь засунуть данные на частоты выше 4 кГц. (Мои фильтры не идеальны, но они достаточно похожи на POTS с относительно небольшим количеством нажатий.)
  • Если вашему протоколу модема требуется короткая преамбула (не более 1 секунды) для синхронизации / калибровки приемника, он не штрафуется.
  • Если возможно, пожалуйста, разместите аудиофайл в доступном месте, чтобы мы могли прослушать какофонию звуковых сигналов и сигналов.

пример

Вот пример записной книжки, которая демонстрирует модуляцию / демодуляцию с помощью простого «включения-выключения» (включая аудио образцы!).

Это будет оценка 100 (бит / сек). Обратите внимание, что он передает с намного худшим SNR 5 дБ.

Ник Т
источник
2
Отличается ли это от обычной задачи «сжать эти двоичные данные»? Если да, не могли бы вы уточнить, насколько точно он отличается?
Дверная ручка
1
Здесь вы модулируете данные (превращаете их во что-то аналоговое), а затем наоборот. Можно было бы назвать это «аналоговым сжатием»
Ник Т
Извините, я не уверен, что понимаю, как работает этот вызов. Слово «модулировать» даже не появляется в статье Википедии, на которую вы ссылаетесь. Не могли бы вы включить дополнительную справочную информацию или уточнить спецификации?
Дверная ручка
4
wget wikipedia.org/Special:Random | grep title | texttospeech audio.wav speechtotext POTSaudio.wav | wget wikipedia/wiki/$text
TessellatingHeckler
1
Это потрясающий вызов, я постараюсь найти время, чтобы отправить ответ!
GoatInTheMachine

Ответы:

7

MATLAB, 1960 бит / с

Вот моя обновленная попытка:

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

С моей первой попытки я немного поиграл. В начале есть небольшая преамбула (18-битные периоды, но может быть короче), которая содержит только косинусную волну. Я извлек это и воспроизвел его, чтобы создать правильно фазированные носители синуса и косинуса для демодуляции - поскольку это очень короткая преамбула, я не учел это в скорости передачи данных согласно вашим инструкциям.

Также с первой попытки я теперь использую созвездие QAM8 для получения 3 битов на символ вместо 2. Это эффективно удваивает скорость передачи. Так что с несущей ~ 2,4 кГц я сейчас достигаю 1960 бит / с.

Я также улучшил обнаружение символов, так что на усреднение не влияют медленные времена нарастания, вызванные фильтрацией - в основном только вторая половина каждого битового периода усредняется, чтобы устранить влияние времен нарастания.

До сих пор далеко от теоретической пропускной способности канала 40 кбит / с из теории Шеннона-Хартли (при условии SNR 30 дБ)

Только для тех, кто любит ужасные звуки, это новая запись:


И в случае, если кому-то интересно, это предыдущая запись 960bps

Том Карпентер
источник
Скоринг - это всего лишь скорость передачи, так что держите код чистым. Я добавил предложение разместить ваш аудиофайл где-нибудь, если это просто для забавы: D
Ник Т
Я выложу аудио на мой сайт. Звучит довольно красиво!
Том Карпентер
Аудио файл @NickT загружен - смотрите ссылку внизу поста.
Том Карпентер
Если у вас есть учетная запись SoundCloud, вы можете загрузить свое аудио и опубликовать ссылку, и она будет воспроизводиться в вашем сообщении. ( Пример )
Увлечения Кэлвина
@NickT спасибо. Я создал аккаунт на Soundcloud и загрузил его. Я также сделал обновленную версию с удвоенной скоростью передачи данных :)
Том Карпентер