Sound Processing IV – Przesyłamy dane szybciej

W tej części cyklu zoptymalizujemy proces wysyłania danych, zapoznamy się z pojęciem kompresji i formatami wymiany danych. Zapraszam.


Na koniec ostatniej części wspomniałem o możliwym sposobie przyśpieszenia transmisji dźwiękiem, czyli wykorzystaniem innego systemu liczbowego którym odwzorowujemy znaki ASCII. Ponieważ plusy przesyłania decymalnie lub hexadecymalnie (szesnastkowo) zamiast binarnie przedstawiłem już w poprzednim poście, przejdę od razu do paru zmian jakie zaszły w kodzie (nie jest ich dużo, wystarczyło 5 minut na odpowiednie zmiany).

Zmiany w kodzie

W klasie Variables zadeklarowałem nowe częstotliwości, tym razem mamy ich więcej, więc ułatwiłem sobie korzystając z tablicy:

variables.pngZmieniona klasa Variables.

Dodatkowo też zadeklarowałem zmienną TOLERANCE z której korzystam w odpowiednio zmodyfikowanym modelu DetectedFrequency – wcześniej była tam hardkodowana wartość 50, tym razem warto ją gdzieś zapisać dla czytelności i łatwiejszych zmian. Zmieniony konstruktor modelu przedstawia się następująco:

detectedfreq

Iteruję po elementach tablicy i sprawdzam czy wykryta częstotliwość zawiera się w przedziale: (częstotliwość z elementu tablicy minus tolerancja, częstotliwość z elementu tablicy plus tolerancja). Jeśłi tak, to przypisuję tę wartość do obiektu, jeśli nie, przechodzę do następnego elementu tablicy. Jeśli nadal po przeiterowaniu nie ma żadnego dopasowanego znaku (porównanie matchedSign == null), to sprawdzam dalej, czy wykryta częstotliwość nie jest pauzą albo początkiem/końcem transmisji. W przeciwnym razie, jeśli jest to szum, przypisuję pusty ciąg znaków.

Do tego takie drobiazgi, jak:

broadcaster.sendMessage(Integer.toString(takeInput()));

Zamiast wcześniejszego:

broadcaster.sendMessage(Integer.toBinaryString(takeInput()));

I analogicznie przy odczytywaniu znaku z liczby:

int code = Integer.parseInt(formattedMessage, 10);

Zamiast wcześniejszego:

int code = Integer.parseInt(formattedMessage, 2);

argument == 10 to wskazanie parserowi że ma do czynienia z liczbą zapisaną decymalnie, argument == 2 – binarnie.

Do tego zmieniłem czas nadawania znaku początku transmisji z 10*INTERVAL na 3*INTERVAL, co również ma duży wpływ na szybkość bez widocznego zwiększenia awaryjności.

Czas na testy

Przesłanie znaku ‘i'(1101001, 7 bitów) zajęło 1578 milisekund.

Dla porównania, kiedy ostatnim razem przesyłaliśmy 7 bitów, zajęło nam to ponad 4 sekundy. Dzielenie 4 przez 1.578 daje 2.53 i tyle razy szybciej wysyłamy znaki teraz.

Tym razem daje to już 1.92 kB na godzinę, więc przesłanie 17 kB zdjęcia kaczki z ostatniego posta zajęłoby już tylko niecałe 9 godzin.

Trochę eksperymentując, ustawiłem czas nadawania pojedyńczej częstotliwości na tylko 50 milisekund. Co oczywiste, skraca to niezawodność, ale w ten sposób przesłałem bez problemu 5 znaków pod rząd osiągając w każdym ~1050 milisekund.

Granicą takiego schodzenia coraz niżej jest niestety nadal błąd dostępu do głośników który sporadycznie się wtedy pojawia i blokuje cały program. Z jednej strony zrozumiałe, w końcu wywołuję dostęp do nich co bardzo krótki czas, a z drugiej raczej muszę robić coś źle, ktoś musiał już przewidzieć takie sytuacje.

Prześlijmy wyraz

Po prostej zmianie polegającej na przyjęciu całego stringa i przesyłaniu go znak po znaku do naszego Broadcastera, wyniki przesyłania ciągu “napis ćwiczebny” są następujące:

formatted

Litery ‘i’ i ‘z’ nie przesłały się pomyślnie, a sama transmisja zajęła aż 26 sekund. O ile więc nie znajdę sposobu rozwiązania problemu czasu dostępu do głośników, podczas przesyłania wyników gry między komputerami będzie można zaparzyć sobie herbatę.

Co jeszcze może pomóc?

Kompresja, chociaż nie w każdym przypadku. Zadziała wtedy, kiedy mamy do czynienia z tekstem w którym pewne ciągi pojawiają się wielokrotnie w tym samym ułożeniu. Dobry przykład jest tutaj.
Mało prawdopodobne jest, żeby wśród przesyłanych przez 2 gry danych pojawiał się aż tak podatny materiał,  dlatego ten sposób zostawię na koniec pracy z naszym modemem.

Do głowy przychodzi mi już tylko zakodowanie pewnych często powtarzających się znaków/kombinacji jako dodatkowe częstotliwości, co rzeczywiście mogłoby znacząco skrócić czas nadawania.

W jaki sposób będziemy wymieniać dane między grami?

Przesyłanie zdań albo liter to jedno, a przesłanie powiązanych ze sobą zmiennych i ich wartości, a później dopasowanie ich to drugie. Moim pomysłem jest skorzystanie z gotowego formatu wymiany danych, który zrobi to za mnie – JSON. Korzystałem z niego już przy okazji przesyłania testów między serwerem a klientem w aplikacji Speechlist (odsyłam do poświęconemu jej posta).

Dla przykładu skorzystam z webowego generatora jsonów:

json

Po przesłaniu ciągu znaków:

{“pozycjaGraczaX”: 10,”pozycjaGraczaY”: 20,”punktyZdrowia”: 99,”ekwipunek”: {“jabłko”: 10,”pieniądze”: 40,”miecz”: 1}}

Program mógłby oddzielić odpowiednie zmienne i załadować je do ich odpowiedników w swojej pamięci. Dodatkowo, kiedy będę chciał odwzorować jakikolwiek swój obiekt w postaci jsona, są już do tego odpowiednie biblioteki (np. gson), które przyjmując obiekt w argumencie oddadzą go w postaci stringa.

Co dalej

W następnej części postaram się usunąć ograniczenie jakie narzuca czas dostępu do głośników i przesłać jakiegoś jsona między komputerami. Do tego przydałby się jakiś system kontroli błędów.

Potem została do napisania już tylko gra wykorzystująca modem.

Odnośniki:

Jak zawsze, kod dostępny jest na GitHubie:

https://github.com/dbeef/soundcoding

dsp2017-1

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s