from util import set_style
set_style()
W ubiegłym stuleciu wokół nas dominowały sygnały analogowe. Przetwarzanie takich sygnałów wymagało specjalistycznej wiedzy z zakresu elektroniki, konstruowania obwodów z elementów elektronicznych, badania przebiegów na oscyloskopie, itp. Sygnały analogowe nadal są stosowane. W ostatnich dwóch dekadach 20. wieku miała miejsce rewolucja, polegająca na wprowadzeniu techniki cyfrowej, również pod postacią komputerów osobistych. Przetwarzanie sygnałów w dziedzinie cyfrowej stało się dostępne praktycznie dla każdego, wystarczy komputer, język programowania i sposób pozyskania sygnału w postaci zer i jedynek. Potrzebna jest również wiedza, ale (o czym wiele osób zapomina) nie tylko na temat samych metod przetwarzania sygnałów. Podstawowe metody (algorytmy) są dla nas dostępne np. w postaci gotowych funkcji w bibliotekach programistycznych. Potrzebne jest jednak zrozumienie zasady działania tych narzędzi i umiejętność ich wykorzystania w praktycznych zastosowaniach. Niniejszy mini podręcznik ma za zadanie pokazanie w jaki sposób można praktycznie zastosować algorytmy cyfrowego przetwarzania, wykorzystując do tego język programowania Python.
Potencjalni czytelnicy podręcznika to zarówno studenci uczelni technicznych, jak i osoby samodzielnie uczące się technik cyfrowego przetwarzania sygnałów (CPS), do celów zawodowych lub hobbystycznych. Podręcznik był pisany z myślą o osobach, które mają podstawową wiedzę z zakresu CPS, zatem podaje bardzo ogólny opis prostszych algorytmów (takich jak analiza FFT czy filtry cyfrowe), zakładając, że nie jest potrzebne tłumaczenie tych pojęć od zera. Osoby zupełnie początkujące powinny uzupełnić swoją wiedzę korzystając z dostępnych podręczników oraz zasobów Internetu. W szczególności, podręcznik jest kierowany do osób znających podstawy programowania w języku Python i chcących wykorzystać ten język do tworzenia algorytmów CPS. Jednak również osoby nie znające Pythona mogą skorzystać z przedstawianych przykładów.
Na rynku jest wiele książek omawiających szczegółowo CPS. Jednak autor zauważa pewną lukę: brakuje „pomostu” pomiędzy książkami opisującymi teorię, a praktycznymi implementacjami algorytmów CPS w języku Python. Podręczniki do CPS są bardzo szczegółowe w opisie podstaw teoretycznych, natomiast nie zawsze pokazują praktyczne zastosowania danych algorytmów. Ponadto autor tego podręcznika ma wrażenie, że wiele książek jest pisanych „przez matematyków dla matematyków”, są przeładowane wzorami, licznymi całkami, itp., a w tym wszystkim gubi się zrozumienie zasady działania algorytmu pod kątem praktycznego zastosowania. Wiele książek tkwi „mentalnie” w okresie „przedkomputerowym”, gdy splot sygnałów lub projekt filtru wykonywało się na kartce papieru. Dzisiaj mamy do dyspozycji programy komputerowe, potrzebna jest jednak wiedza w jaki sposób ich użyć, aby uzyskać pożądany efekt.
Autor tego podręcznika z własnego doświadczenia wie, że studentów na uczelniach technicznych zasypuje się teorią pełną wzorów. Student uczy się tej teorii na pamięć aby „zaliczyć przedmiot”, często nie rozumiejąc jak dany algorytm działa. Wiedza ta oczywiście „wyparowuje” po egzaminie. Tymczasem rzeczywistość jest inna. Aby np. przeprowadzić operację analizy częstotliwościowej metodą FFT, nie trzeba pamiętać wzoru na przekształcenie Fouriera (autor również go nie pamięta), ale trzeba wiedzieć jaki rozmiar i typ okna dobrać, jak obliczyć częstotliwości składowych widmowych, itp. Efekt jest taki, że student zalicza przedmiot na Politechnice, a następnie nie potrafi np. zaimplementować filtru cyfrowego typu FIR, ponieważ zdążył już zapomnieć „wykutą” teorię, a nie zrozumiał prostej zasady działania algorytmu.
Niniejszy podręcznik przedstawia podstawy teoretyczne „w telegraficznym skrócie”, posiłkując się prostymi wzorami tylko dla ilustracji algorytmów (całek nie będzie), skupia się natomiast na pokazaniu implementacji algorytmu w formie kodu, przedstawieniu wyników w postaci wykresów oraz na omówieniu praktycznych zastosowań danego algorytmu. Do implementacji algorytmów został wykorzystany język Python.
Na uczelniach technicznych od samego początku nauczania CPS niepodzielnie króluje Matlab. W początkowym okresie nie było dla niego żadnej alternatywy. Obecnie sytuacja jest inna, użytkownicy mają do wyboru w pełni darmowe narzędzia. Jednak uczelnie wciąż upierają się przy Matlabie, który ma dwie zasadnicze wady. Po pierwsze, licencja na program jest bardzo droga (wersja dla użytkowników prywatnych: ok. 120 Euro), poza zasięgiem studentów, wskutek czego studenci nie mogą legalnie używać Matlaba na swoich komputerach. Każdy dodatkowy toolbox, np. Signal Processing, to kolejny wydatek. Darmowe odpowiedniki (Octave, SciLab) nie są w pełni kompatybilne z Matlabem. Po drugie, Matlab używa archaicznego języka programowania, wywodzącego się z Fortrana, zupełnie niepodobnego do współcześnie stosowanych języków, co sprawia trudności osobom programującym w innych językach.
Python jest skryptowym językiem programowania do ogólnych zastosowań. Jaka jest przewaga Pythona nad Matlabem?
W jakich aspektach Python ustępuje Matlabowi?
Pod względem szybkości działania algorytmów CPS, oba narzędzia są porównywalne, ponieważ oba używają podobnych bibliotek napisanych w języku C (np. BLAS). Oba języki są typu skryptowego, przez co wykonywanie operacji krok po kroku (np. w pętli) jest znacznie wolniejsze niż dla analogicznego kodu napisanego w języku kompilowanym.
Na wstępie należy zaznaczyć, że dostępne są dwie wersje interpretera języka Python: obecna wersja 3 i starsza wersja 2.7. Wersje te nie są ze sobą kompatybilne. Zaleca się zainstalowanie najnowszej wersji 3, wszystkie kody w tym podręczniku zostały napisane dla wersji 3.6. Instalatory są dostępne osobno dla wersji 32 i 64 bitowej, w większości przypadków potrzebna będzie wersja 64-bitowa.
Początkującym użytkownikom pracującym w systemie Windows można polecić dystrybucję Anaconda. Zawiera ona interpreter języka Python oraz zestaw najczęściej stosowanych modułów, w tym moduły wykorzystywane w niniejszym podręczniku, nie trzeba zatem instalować modułów osobno. Do instalacji pakietów służy program conda. Wadą Anacondy jest instalowanie modułów, które nie będą wykorzystywane, oraz dość wolne aktualizacje do nowych wersji interpretera Python.
Aby zainstalować sam interpreter w systemie Windows, należy pobrać plik instalacyjny z oficjalnej strony python.org. Z niezrozumiałych powodów, duży przycisk pod tekstem Download for Windows pobiera zawsze wersję 32-bitową. Aby pobrać wersję 64-bitową, należy wybrać: Downloads, Windows, a następnie z listy wybrać Download Windows x86-64 web-based installer dla wersji opisanej jako Latest Python 3 Release. Następnie należy uruchomić pobrany instalator. UWAGA: w instalatorze należy zaznaczyć opcję Add Python to PATH. Opcja ta nie jest domyślnie zaznaczona, wskutek czego dostajemy instalację bez możliwości prostego uruchamiania Pythona i narzędzia pip. Katalog instalacyjny interpretera oraz zawarty w nim podkatalog Scripts muszą być na ścieżce systemowej PATH. Ponadto, jeżeli instalujemy interpreter w trybie zaawansowanym, musimy upewnić się że jest wybrany moduł Tk.
W systemie operacyjnym Linux mamy już standardowo zainstalowany Python w obu wersjach. Domyślną wersją jest wersja 2, jest ona uruchamiania przy wywołaniu komendy python. Aby użyć wersji 3, należy podać komendę python3 (prawdopodobnie nie będzie to najnowsza wersja), albo użyć konkretnej wersji za pomocą komendy np. python3.6 (być może najnowszą wersję trzeba zainstalować korzystając z narzędzia do instalacji pakietów.
Interpreter zainstalowany z oficjalnej strony zawiera tylko podstawowe moduły. Pozostałe należy zainstalować samodzielnie. Służy do tego program pip. Aby zainstalować moduł, należy wykonać polecenie:
pip install nazwa_modułu
W systemie Linux trzeba użyć wersji pip dla konkretnej wersji Pythona (pip3 dla wersji uruchamianej komendą python3, pip3.6 dla python3.6, itd. Instalacja modułów w systemie Linux wymaga praw administratora (sudo), lepszym pomysłem jest instalacja modułów w katalogu domowym użytkownika – należy podać dodatkowo opcję --user. Innym, często zalecanym rozwiązaniem jest zastosowanie wirtualnego środowiska (virtual environment). W systemie Windows, jeżeli Python został zainstalowany w katalogu systemowym (Program Files), również potrzebne są uprawnienia administratora, dlatego lepiej jest zainstalować interpreter w katalogu użytkownika.
Jeżeli w systemie Windows dostajemy komunikat typu „'pip' is not recognized as an internal or external command, operable program or batch file”, to oznacza że nie pozwoliliśmy instalatorowi na dodanie katalogu do zmiennej PATH. Teraz albo musimy zrobić to sami, albo musimy podać pełną ścieżkę do programu pip.exe.
Dla potrzeb tego podręcznika, o ile nie używamy dystrybucji Anaconda, należy zainstalować następujące moduły (wymienione są nazwy, które należy podać do komendy pip install).
numpy – NumPy (Numerical Python), moduł do obliczeń matematycznych.scipy – SciPy (Scientific Python), bardziej „naukowe” funkcje, w tym do przetwarzania sygnałów.matplotlib – moduł do tworzenia wykresów.jupyter – Jupyter Notebook, opcjonalny moduł do uruchamiania tego podręcznika w wersji elektronicznej.Wszystkie powyższe moduły są dla systemu Windows dostarczane w plikach whl, czyli skompilowanych i przygotowanych do instalacji. Same moduły są napisane w języku C i wymagają kompilacji, co wykonali już autorzy modułów. W przypadku instalacji innych modułów może zdarzyć się, że mamy tylko kod źródłowy. Instalacja takiego modułu wymaga obecności na komputerze środowiska Microsoft Visual Studio w wersji co najmniej 2015. W takich sytuacjach warto zajrzeć na stronę Cristopha Gohlke: https://www.lfd.uci.edu/~gohlke/pythonlibs/. Zawiera ona wiele skompilowanych modułów, należy wybrać plik dla właściwej wersji zainstalowanego interpretera, pobrać plik whl na komputer i zainstalować go komendą pip install nazwa_pliku.whl. Wersja modułu NumPy dostępna na tej stronie została skompilowana z użyciem zoptymalizowanej biblioteki Intel MKL, podczas gdy oficjalna wersja używa darmowej biblioteki OpenBLAS. Można więc wybrać wersję MKL, pobierając ją z powyższej strony.
Filozofia języka Python jest taka, że kod jest tworzony z użyciem dowolnego edytora tekstowego, a następnie jest on uruchamiany z wiersza poleceń komendą:
python nazwa_pliku.py
(w systemie Linux należy użyć komendy dla konkretnej wersji interpretera). Do tworzenia kodu można użyć dowolnego edytora tekstowego. Wskazane jest, aby miał on takie funkcje, jak możliwość uruchamiania skryptu za pomocą kombinacji klawiszy, podświetlanie składni, automatyczne uzupełnianie czy możliwość łatwego uruchamiania zewnętrznych narzędzi, np. do sprawdzania składni. Może to być np. popularny edytor Notepad++. Bardzo ważne jest skonfigurowanie edytora w taki sposób, aby dla kodu w języku Python, naciśnięcie klawisza Tab powodowało wpisanie czterech spacji. W języku Python, wcięcia kodu definiują jego strukturę i muszą one być jednolite, standardem są cztery spacje na jeden poziom wcięcia.
Poniżej podano kilka sugestii edytorów dla początkujących użytkowników, zwłaszcza dla tych, którzy są przyzwyczajeni do pracy z Matlabem.
pip install spyder. Konfiguracja edytora ma przypominać środowisko Matlab. Spyder jest jednak przeraźliwie wolny.Alternatywnym podejściem jest zastosowanie narzędzia Jupyter Notebook opisanego poniżej.
Jupyter Notebook prezentuje odmienne podejście do tworzenia kodu w języku Python. Istotą tego narzędzia są interaktywne dokumenty, które łączą kod Pythona, wyniki jego działania, tekst, grafikę, dźwięk, itp. Pozwala to na łatwe łączenie kodu z tekstowym opisem oraz na czytelną prezentację wyników. Z tych względów, Jupyter Notebook bardzo dobrze nadaje się do zastosowań związanych z cyfrowym przetwarzaniem sygnałów. Niniejszy podręcznik został w całości utworzony przy pomocy tego narzędzia. Jupyter Notebook instaluje się tak jak inne moduły: pip install jupyter. Dystrybucja Anaconda już zawiera ten moduł.
Jupyter Notebook działa w przeglądarce internetowej. Uruchomienie dokumentu wymaga wpisania w wierszu poleceń komendy:
jupyter notebook nazwa_dokumentu.ipynb
Dokument powinien zostać otwarty w domyślnej przeglądarce internetowej. Treść dokumentu jest podzielona na komórki. Przetworzenie całego dokumentu jest możliwe przez wybranie z menu Cell komendy Run all. Zwykle jednak uruchamia się osobno poszczególne komórki. Każda komórka może być w trybie edycji (zielony margines z lewej strony) lub w trybie komend (niebieski margines). Przejście do edycji komórki następuje po dwukrotnym kliknięciu myszą na komórkę, wyjście z trybu edycji wymaga wciśnięcia klawisza Esc. Aby uruchomić kod w zaznaczonej komórce, wciskamy klawisze Control+Enter lub Shift+Enter (ta druga kombinacja automatycznie przechodzi do kolejnej komórki). Wstawienie nowej komórki jest możliwe w trybie komend za pomocą klawisza B (poniżej zaznaczonej komórki) lub A (powyżej). Klawisz H w trybie komend wyświetla listę skrótów klawiszowych.
Uruchomienie komórki powoduje wykonanie kodu i wyświetlenie poniżej wyników. Komórka z kodem zostaje opatrzona symbolem In[] z numerem w nawiasach, komórka z wynikami: Out[] z tym samym numerem. Każde uruchomienie komórki zwiększa numer. Należy pamiętać, że kolejność komórek jest wyznaczona przez ich numery, a nie położenie w dokumencie. Struktura kodu w dokumencie jest taka sama, jak w skrypcie powstałym przez połączenie wszystkich komórek, uszeregowanych w rosnącej kolejności ich numerów.
Dokumenty mają włączoną opcję automatycznego zapisywania, jednak warto pamiętać o zapisywaniu zmian co jakiś czas, a w szczególności przed zamknięciem dokumentu.
Dokumenty stanowiące treść tego podręcznika są dostępne do pobrania. Autor zachęca czytelników do samodzielnego uruchomienia tych dokumentów oraz do własnych eksperymentów.
Każdy rozdział podręcznika jest zawarty w osobnym dokumencie programu Jupyter Notebook. Pierwsza komórka dokumentu zawsze zawiera instrukcje wczytujące wykorzystywane moduły. W przypadku modułów takich jak NumPy, stosuje się zwyczajowo aliasy (skróty nazw) modułów: import numpy as np. Dzięki temu, można się odwoływać do funkcji z tego modułu za pomocą skrótu np. W podobny sposób, autor stosuje skrót sig dla często stosowanego modułu scipy.signal. W opisach tekstowych autor starał się podawać pełne nazwy.
Kolejna komórka każdego dokumentu zawiera instrukcje formatujące dokument (ustawiające style), tę komórkę należy pominąć przenosząc kod do zwykłego skryptu poza środowiskiem Jupyter. Następnie zwykle definiowane są stałe, takie jak częstotliwość próbkowania (w większości przykładów stosowana jest częstotliwość (48 kHz). Wymienione powyżej komórki muszą być uruchomione w pierwszej kolejności. Dalsze komórki zawierają opisy oraz kod przykładów. Dokumenty są utworzone w taki sposób, że komórki powinny być wykonywane w kolejności, w jakiej są umieszczone w dokumencie. Aby uniknąć powtarzania kodu, często komórka wykorzystuje zmienne utworzone we wcześniejszych komórkach.
Tekst podręcznika został napisany w języku Markdown. Autor starał się podawać odnośniki do dokumentacji dla wykorzystanych funkcji z niestandardowych modułów, a także odnośniki do wybranych haseł z Wikipedii (niestety, polska wersja jest uboga w opisy pojęć z dziedziny CPS) oraz do stron, z których materiały zostały wykorzystane w podręczniku.