Створіть (програмний) модем!


14

Об'єктивна

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

Кроки

  1. Створити якийсь випадковий (/dev/random або подібних) даних, які потребуватимуть 3-4 секунди для передачі
  2. Модулюйте дані за допомогою модулятора, щоб створити звуковий файл
  3. Передайте аудіофайл через симулятор POTS . Якщо у вас немає Python / Scipy, ви можете завантажити файл із формою або зробити запит API JSON.
  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

МАТЛАБ, 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 кГц я зараз досягаю 1960bps.

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

Все ще ніде поблизу теоретичної пропускної здатності каналу 40 кбіт / с від теорії Шеннона-Хартлі (якщо вважати SNR 30 дБ)

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


І якщо когось цікавить, це попередній запис 960bps


Підрахунок балів - це лише швидкість передачі, тому нехай ваш код чистий. Я додав пропозицію розмістити свій аудіофайл де-небудь, якщо це легко для фунсій: D
Nick T

Я завантажу аудіо на свій сайт. Це звучить досить кричуще!
Том Карпентер

Завантажений аудіофайл @NickT - дивіться посилання внизу публікації.
Том Карпентер

Якщо у вас є обліковий запис SoundCloud, ви можете завантажити аудіо та розмістити посилання, і воно буде відтворюватися у вашому дописі. ( Приклад )
Захоплення Кальвіна

@NickT спасибі Я створив обліковий запис soundcloud і завантажив його. Я також зробив оновлену версію з подвоєною швидкістю передачі даних :)
Том Карпентер
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.