Dzień dobry.
Ostatnie kilka lat poświęciłem badaniu i tworzeniu różnych algorytmów przetwarzania sygnałów w adaptacyjnych siatkach antenowych, i w ramach mojej pracy kontynuuję te działania. Chciałbym podzielić się wiedzą i wskazówkami, które odkryłem. Mam nadzieję, że będą one przydatne dla osób początkujących w nauce o przetwarzaniu sygnałów lub po prostu ciekawych tego tematu.
Co to jest adaptacyjna siatka antenowa?
– to zbiór elementów antenowych, w pewien sposób umieszczonych w przestrzeni. Uproszczoną strukturę adaptacyjnej siatki antenowej, którą będziemy rozważać, można przedstawić w następujący sposób:
Adaptacyjne siatki antenowe często nazywane są «inteligentnymi» antenami (). „Inteligentna” siatkę antenową czyni blok przetwarzania sygnału i algorytmy, które w nim zaimplementowano. Algorytmy te analizują odbierany sygnał i formują zestaw współczynników wagowych $inline$w_1…w_N$inline$, które określają amplitudę i początkową fazę sygnału dla każdego z elementów. Określone rozkład amplitudowo-fazowy definiuje całej siatki jako całości. Możliwość syntezowania diagramu kierunkowości o wymaganym kształcie i jego zmiana w procesie przetwarzania sygnału — to jedna z głównych cech adaptacyjnych siatek antenowych, pozwalająca na rozwiązanie szerokiego . Ale po kolei.
Jak formowany jest diagram kierunkowości?
charakteryzuje moc sygnału emitowanego w danym kierunku. Dla uproszczenia przyjmijmy, że elementy siatki są izotropowe, tzn. moc emitowanego sygnału nie zależy od kierunku. Wzmocnienie lub osłabienie mocy emitowanej przez siatkę w danym kierunku wynika z EMW emitowanych przez różne elementy siatki antenowej. Stabilny obraz interferencyjny dla EMW jest możliwy tylko w warunkach ich , tzn. różnica faz sygnałów nie powinna zmieniać się w czasie. W idealnym przypadku każdy z elementów siatki antenowej powinien emitować na tej samej nośnej częstotliwości $inline$f_{0}$inline$. Jednak w praktyce musimy pracować z sygnałami wąskopasmowymi, mającymi spektrum o ograniczonej szerokości $inline$Delta f << f_{0}$inline$.
Niech wszystkie elementy AР emitują ten sam sygnał z $inline$x_n(t)=u(t)$inline$. Wtedy na odbiorniku sygnał odebrany od n-tego elementu można przedstawić w $$display$$a_n(t) = u(t-tau_n)e^{i2pi f_0(t-tau_n)}$$display$$
gdzie $inline$tau_n$inline$ – opóźnienie w rozprzestrzenianiu się sygnału od elementu antenowego do punktu odbioru.
Taki sygnał jest
„kwaziharmoniczny” , a aby spełnić warunek koherentności, maksymalne opóźnienie w rozprzestrzenianiu się EMW między dowolnymi dwoma elementami musi być znacznie mniejsze od charakterystycznego czasu zmiany obwiedni sygnału $inline$T$inline$, tzn. $inline$u(t-tau_n) ≈ u(t-tau_m)$inline$. W ten sposób warunek koherentności dla sygnału wąskopasmowego można zapisać w następujący sposób:$$display$$T≈frac{1}{Delta f}>>frac{D_{max}}{c}=max(tau_k-tau_m) $$display$$
gdzie $inline$D_{max}$inline$ — maksymalne odległość między elementami AР, a $inline$c$inline$ — prędkość światła.
Podczas odbioru sygnału koherentne sumowanie jest przeprowadzane w postaci cyfrowej w bloku przetwarzania przestrzennego. W tym przypadku złożona wartość cyfrowego sygnału na wyjściu tego bloku definiowana jest wyrażeniem:
$$display$$y=sum_{n=1}^Nw_n^*x_n$$display$$
Ostatnie wyrażenie wygodniej przedstawić w postaci
iloczynu skalarnego $$display$$y=(textbf{w},textbf{x})=textbf{w}^Htextbf{x}$$display$$
— wektory kolumnowe, a $inline$(.)^H$inline$ — operacja hermitowskiego sprzężenia.
gdzie w i x Wektorowe przedstawienie sygnałów jest jednym z podstawowych przy pracy z siatkami antenowymi, ponieważ często pozwala uniknąć skomplikowanych obliczeń matematycznych. Ponadto utożsamienie odebranego w pewnym momencie sygnału z wektorem często pozwala abstrahować od rzeczywistego systemu fizycznego i zrozumieć, co się dzieje z punktu widzenia geometrii. .
fal płaskich
ze wszystkich możliwych kierunków. W tym przypadku wartości elementów wektora $$display$$x_n=s_n=exp{-i(textbf{k}(phi,theta),textbf{r}_n)}$$display$$ x k
wektor falowy
gdzie k – , $inline$phi$inline$ i $inline$theta$inline$ – i , charakteryzujące kierunek napływu fali płaskiej, $inline$textbf{r}_n$inline$ – współrzędna elementu anteny, $inline$s_n$inline$ – element wektora fazowego s fali płaskiej z wektorem falowym k (w literaturze anglojęzycznej wektor fazowy nazywany jest steerage vector). Zależność kwadratu amplitudy wartości y od $inline$phi$inline$ i $inline$theta$inline$ określa diagram kierunkowości siatki antenowej dla odbioru przy danym wektorze wagowym w.
Cechy diagramu kierunkowości siatki antenowej
Wygodnie jest badać ogólne właściwości diagramu kierunkowości siatek antenowych na liniowej siatce antenowej w płaszczyźnie poziomej (tj. DN zależy tylko od kąta azymutalnego $inline$phi$inline$). Jest to wygodne z dwóch punktów widzenia: analitycznych obliczeń i wizualizacji.
Obliczymy DN dla jednostkowego wektora wagowego ($inline$w_n=1, n = 1 … N$inline$), stosując opisaną metodę.
Matematyka tutaj
Projekcja wektora falowego na oś pionową: $inline$k_v=-frac{2pi}{lambda}sinphi$inline$
Pionowa współrzędna elementu anteny o indeksie n: $inline$r_{nv}=(n-1)d$inline$
Tutaj d – okres siatki antenowej (odległość między sąsiednimi elementami), λ — długość fali. Wszystkie inne elementy wektora r są równe zeru.
Sygnał odbierany przez siatkę antenową zapisuje się w następujący sposób:
$$display$$y=sum_{n=1}^{N}1 ⋅exp{i2pi nfrac{d}{lambda}sinphi}$$display$$
Zastosujmy wzór dla i :
$$display$$y=frac{1-exp{i2pi Nfrac{d}{lambda}sinphi}}{1-exp{i2pi frac{d}{lambda}sinphi}}=frac{sin(pi frac{Nd}{lambda}sinphi)}{sin(pi frac{d}{lambda}sinphi)}exp{ipi frac{d(N-1)}{lambda}sinphi}$$display$$
Ostatecznie uzyskamy:
$$display$$F(phi)=|y|^2=frac{sin^2(pi frac{Nd}{lambda}sinphi)}{sin^2(pi frac{d}{lambda}sinphi)} $$display$$
Okresowość diagramu kierunkowości
Uzyskany diagram kierunkowości siatki antenowej jest funkcją okresową od sinusa kąta. Oznacza to, że dla określonych wartości proporcji d/λ ma maksima dyfrakcyjne (dodatkowe).
Nienormowany diagram kierunkowości siatki antenowej dla N = 5
Normowany diagram kierunkowości siatki antenowej dla N = 5 w polarnym układzie współrzędnych
Położenie «dyfrakcyjnych» można zobaczyć bezpośrednio z dla DН. Jednak spróbujemy zrozumieć, skąd one się biorą fizycznie i geometrycznie (w przestrzeni N-wymiarowej).
Elementy wektorów s stanowią zespolone eksponenty $inline$e^{iPsi n}$inline$, wartości których są określone wielkością uogólnionego kąta $inline$Psi = 2pi frac{d}{lambda}sinphi$inline$. Jeśli istnieją dwa uogólnione kąty odpowiadające różnym kierunkom nadejścia płaskiej fali, dla których zachodzi $inline$Psi_1 = Psi_2 + 2pi m$inline$, to oznacza to dwie rzeczy:
- Fizycznie: płaskie fale frontowe, nadchodzące z tych kierunków, indukują na elementach siatki antenowej identyczne rozkłady amplitudowo-fazowe pól elektromagnetycznych.
- Geometrycznie: dla tych dwóch kierunków pokrywają się.
Powiązane w ten sposób kierunki nadejścia fali są z punktu widzenia siatki antenowej ekwiwalentne i nie są od siebie rozróżniane.
Jak określić zakres kątów, w którym zawsze leży tylko jeden główny maksimum DН? Zrobimy to w okolicach zerowego azymutu z następujących względów: wielkość narastania fazy między dwoma sąsiednimi elementami powinna leżeć w przedziale od $inline$-pi$inline$ do $inline$pi$inline$.
$$display$$-pi<2pifrac{d}{lambda}sinphi<pi$$display$$
Rozwiązując to nierówność otrzymujemy warunek na obszar jednoznaczności w okolicach zera:
$$display$$|sinphi|<frac{lambda}{2d}$$display$$
Widać, że rozmiar obszaru jednoznaczności w kącie zależy od proporcji d/λ. Jeśli d = 0.5λ, to każdy kierunek nadejścia sygnału jest „indywidualny”, a obszar jednoznaczności obejmuje pełny zakres kątów. Jeśli zaś d = 2.0λ, to kierunki 0, ±30, ±90 – są ekwiwalentne. Na diagramie kierunkowości pojawiają się plamki dyfrakcyjne.
Zazwyczaj, plamki dyfrakcyjne dążą się do osłabienia za pomocą kierunkowych elementów antenowych. W takim przypadku pełny diagram kierunkowości siatki antenowej jest iloczynem DН jednego elementu i siatki izotropowych elementów. Parametry DН jednego elementu zazwyczaj wybiera się w oparciu o warunek na obszar jednoznaczności siatki antenowej.
Szerokość głównego płatka
wzór inżynieryjny do oceny szerokości głównego płata systemu antenowego: $inline$Delta phi ≈ frac{lambda}{D}$inline$, gdzie D to charakterystyczny rozmiar anteny. Wzór ten jest stosowany w różnych typach anten, w tym w antenach parabolicznych. Pokażemy, że jest on również stosowany w przypadku siatek antenowych.
Określamy szerokość głównego płata przy pierwszych zerach funkcji w pobliżu głównego maksimum. Licznik dla $inline$F(phi)$inline$ przyjmuje wartość zero przy $inline$sinphi=mfrac{lambda}{dN}$inline$. Pierwsze zera odpowiadają m = ±1. $inline$frac{lambda}{dN}<<1$inline$ otrzymujemy $inline$Delta phi = 2frac{lambda}{dN}$inline$.
Zwykle szerokość kierunkowości AR określa się na poziomie połowy mocy (-3 dB). W tym przypadku używa się wyrażenia:
$$display$$Delta phi≈0.88frac{lambda}{dN}$$display$$
Przykład
Szerokością głównego płata można sterować, ustalając różne wartości amplitud dla współczynników wagowych siatki antenowej. Rozważmy trzy rozkłady:
- Jednolity rozkład amplitudy (weights 1): $inline$w_n=1$inline$.
- Malejące wartości amplitudy w kierunku brzegów siatki (weights 2): $inline$w_n=0.5+0.3cos(2pifrac{n-1}{N}-pifrac{N-1}{N})$inline$
- Rosnące wartości amplitudy w kierunku brzegów siatki (weights 3): $inline$w_n=0.5-0.3cos(2pifrac{n-1}{N}-pifrac{N-1}{N})$inline$
Na rysunku przedstawione są uzyskane znormalizowane diagramy kierunkowości w skali logarytmicznej:
Z rysunku można zauważyć następujące tendencje: malejący rozkład amplitud współczynników wagowych w kierunku brzegów siatki prowadzi do poszerzenia głównego płata kierunkowości, ale zmniejszenia poziomu bocznych płatów. Rosnące wartości amplitud w kierunku brzegów antenowej siatki, przeciwnie, prowadzą do zwężenia głównego płata i zwiększenia poziomu płatów bocznych. Warto rozważyć przypadki skrajne:
- Amplitudy współczynników wagowych wszystkich elementów, z wyjątkiem skrajnych, są równe zeru. Wagi dla skrajnych elementów są równe jedności. W tym przypadku siatka staje się równoważna dwuelementowej antenie AR o okresie D = (N-1)d. Nie trudno oszacować z powyższego wzoru szerokość głównego płata. W tym przypadku płaty boczne przekształcą się w maksima dyfrakcyjne i wyrównają się poziomem z głównym maksimum.
- Waga centralnego elementu wynosi jeden, a pozostałych – zero. W tym przypadku uzyskujemy w rzeczywistości jedną antenę o izotropowym diagramie kierunkowości.
Kierunek głównego maksimum
Zatem przyjrzeliśmy się, jak można regulować szerokość głównego płata charakterystyki kierunkowej. Teraz zobaczymy, jak kierować kierunkiem. Przypomnijmy sobie dla sygnału akceptowanego. Załóżmy, że chcemy, aby maksimum charakterystyki kierunkowej wskazywało w pewnym kierunku $inline$phi_0$inline$. Oznacza to, że z tego kierunku powinna być odbierana maksymalna moc. W tym kierunku odpowiada fazowy wektor $inline$textbf{s}(phi_0)$inline$ w N-wymiarowej przestrzeni wektorowej, a odbierana moc definiowana jest jako kwadrat iloczynu skalarnego tego fazowego wektora z wektorem współczynników wagowych w. Iloczyn skalarny dwóch wektorów jest maksymalny, gdy są one , tj. $inline$textbf{w}=beta textbf{s}(phi_0)$inline$, gdzie β – pewien czynnik normalizujący. Tak więc, jeśli wybierzemy wektor wagowy równy fazowemu dla wymaganego kierunku, maksymalny płat charakterystyki kierunkowej zostanie obrócony.

Rozważmy jako przykład następujące współczynniki wagowe: $inline$textbf{w}=textbf{s}(10°)$inline$
$$display$$w_n=exp{i2pifrac{d}{lambda}(n-1)sin(10pi/180)}$$display$$
W rezultacie otrzymamy charakterystykę kierunkową z głównym maksimum w kierunku 10°.
Teraz zastosujemy te same współczynniki wagowe, ale nie do odbioru sygnału, lecz do jego nadawania. Należy pamiętać, że podczas nadawania sygnału kierunek wektora falowego zmienia się na przeciwny. Oznacza to, że elementy dla odbioru i nadawania różnią się znakiem w wykładniku funkcji eksponencjalnej, tj. są ze sobą powiązane sprzężeniem zespolonym. W rezultacie otrzymamy maksimum charakterystyki kierunkowej dla nadawania w kierunku -10°, co nie pokrywa się z maksimum DN dla odbioru przy tych samych współczynnikach wagowych. Aby naprawić sytuację, należy również zastosować sprzężenie zespolone do współczynników wagowych.

Opisaną cechę formowania DN dla odbioru i nadawania należy zawsze mieć na uwadze podczas pracy z siatkami antenowymi.
Zabawy z charakterystyką kierunkową
Kilka maksimów
Postawmy zadanie stworzenia dwóch głównych maksimów charakterystyki kierunkowej w kierunkach: -5° i 10°. W tym celu wybierzemy jako wektor wagowy ważoną sumę wektorów fazowych dla odpowiadających kierunków.
$$display$$textbf{w} = betatextbf{s}(10°)+(1-beta)textbf{s}(-5°)$$display$$
Dostosowując współczynnik β można regulować proporcję między głównymi płatkami. Tutaj ponownie warto spojrzeć na to, co dzieje się w przestrzeni wektorowej. Jeśli β jest większe niż 0,5, to wektor współczynników wagowych leży bliżej s(10°), w przeciwnym razie bliżej s(-5°). Im bliżej wektor wagowy jest do jednego z fazorów, tym większa wartość odpowiadającego skalarnego iloczynu, a co za tym idzie, wielkość odpowiadającego maksimum DI.

Należy jednak wziąć pod uwagę, że oba główne płatki mają skończoną szerokość i jeśli zechcemy ustawić się na dwa bliskie kierunki, to te płatki połączą się w jeden, skierowany na pewien średni kierunek.
Jedno maksimum i zero
Teraz spróbujemy ustawić maksimum diagramu kierunkowości na kierunek $inline$phi_1=10°$inline$ i jednocześnie stłumić sygnał przychodzący z kierunku $inline$phi_2=-5°$inline$. W tym celu konieczne jest ustawienie zera DI dla odpowiedniego kąta. Można to zrobić w następujący sposób:
$$display$$textbf{w}=textbf{s}_1-frac{textbf{s}_2^Htextbf{s}_1}{N}textbf{s}_2$$display$$
gdzie $inline$textbf{s}_1 = textbf{s}(10°)$inline$, a $inline$textbf{s}_2 = textbf{s}(-5°)$inline$.

Geometryczne znaczenie wyboru wektora wagowego jest następujące. Chcemy, aby ten wektor w miał maksymalną projekcję na $inline$textbf{s}_1$inline$ i jednocześnie był ortogonalny do wektora $inline$textbf{s}_2$inline$. Wektor $inline$textbf{s}_1$inline$ można przedstawić jako sumę dwóch składników: wektora kolinearnym $inline$textbf{s}_2$inline$ oraz wektora ortogonalnego $inline$textbf{s}_2$inline$. Aby spełnić postawione zadanie, należy wybrać drugi składnik jako wektor współczynników wagowych w. Składnik kolinearny można obliczyć, projektując wektor $inline$textbf{s}_1$inline$ na znormalizowany wektor $inline$frac{textbf{s}_2}{sqrt{N}}$inline$ przy użyciu iloczynu skalarnego.
$$display$$textbf{s}_{1||}=frac{textbf{s}_2}{sqrt{N}}frac{textbf{s}_2^Htextbf{s}_1}{sqrt{N}}$$display$$
Odpowiednio, odejmując od pierwotnego wektora fazującego $inline$textbf{s}_1$inline$ jego składnik kolinearny, uzyskamy poszukiwany wektor wagowy.

Kilka dodatkowych uwag
- W poprzednich częściach poruszyłem kwestię normalizacji wektora wag, tzn. jego długości. Otóż normalizacja wektora wag nie ma wpływu na charakterystyki diagramu promieniowania siatki antenowej: kierunek głównego maksimum, szerokość głównego płatka itp. Można również wykazać, że ta normalizacja nie wpływa na OSH na wyjściu bloku przetwarzania przestrzennego. W związku z tym przy rozważaniu algorytmów przetwarzania przestrzennego sygnału zwykle przyjmuje się jednostkową normalizację wektora wag, tzn. $inline$textbf{w}^Htextbf{w}=1$inline$
- Możliwości formowania DN siatki antenowej są określane przez liczbę elementów N. Im więcej elementów, tym szersze możliwości. Tym większa liczba stopni swobody przy realizacji przestrzennego przetwarzania wag, więcej opcji jak "obracać" wektorem wag w przestrzeni N-wymiarowej.
- Podczas odbioru DN siatki antenowej fizycznie nie istnieje, a wszystko to istnieje jedynie w "wyobraźni" bloku obliczeniowego, który przetwarza sygnał. Oznacza to, że w tym samym momencie można syntetyzować kilka DN i prowadzić niezależne przetwarzanie sygnałów pochodzących z różnych kierunków. W przypadku transmisji wszystko jest nieco trudniejsze, jednak również istnieje możliwość syntetyzowania kilku DN do przesyłania różnych strumieni danych. Taka technologia w systemach komunikacyjnych została nazwana .
- Dzięki przedstawionemu kodowi matlab można samodzielnie eksperymentować z DN.
Kod% ustawienia siatki antenowej N = 10; % liczba elementów d = 0.5; % okres siatki antenowej wLength = 1; % długość fali tryb = 'receiver'; % odbiornik lub nadajnik % wagi siatki antenowej w = ones(N,1); % w = 0.5 + 0.3*cos(2*pi*((0:N-1)-0.5*(N-1))\/N).'; % w = 0.5 - 0.3*cos(2*pi*((0:N-1)-0.5*(N-1))\/N).'; % w = exp(2i*pi*d\/wLength*sin(10\/180*pi)*(0:N-1)).'; % b = 0.5; w = b*exp(2i*pi*d\/wLength*sin(+10\/180*pi)*(0:N-1)).' + (1-b)*exp(2i*pi*d\/wLength*sin(-5\/180*pi)*(0:N-1)).'; % b = 0.5; w = b*exp(2i*pi*d\/wLength*sin(+3\/180*pi)*(0:N-1)).' + (1-b)*exp(2i*pi*d\/wLength*sin(-3\/180*pi)*(0:N-1)).'; % s1 = exp(2i*pi*d\/wLength*sin(10\/180*pi)*(0:N-1)).'; % s2 = exp(2i*pi*d\/wLength*sin(-5\/180*pi)*(0:N-1)).'; % w = s1 - (1\/N)*s2*s2'*s1; % w = s1; % normalizacja wag w = w.\/sqrt(sum(abs(w).^2)); % zbiór wartości kątowych do obliczenia wzoru angGrid_deg = (-90:0.5:90); % konwersja stopni na radiany angGrid = angGrid_deg * pi \/ 180; % obliczanie zestawu wektorów kierunkowych dla siatki kątowej switch (tryb) case 'receiver' s = exp(2i*pi*d\/wLength*bsxfun(@times,(0:N-1)',sin(angGrid))); case 'transmitter' s = exp(-2i*pi*d\/wLength*bsxfun(@times,(0:N-1)',sin(angGrid))); end % obliczanie wzoru y = (abs(w'*s)).^2; % skala liniowa plot(angGrid_deg,y\/max(y)); grid on; xlim([-90 90]); % skala logarytmiczna % plot(angGrid_deg,10*log10(y\/max(y))); % grid on; % xlim([-90 90]);
Jakie zadania można rozwiązać za pomocą adaptacyjnej siatki antenowej?
Optymalne odbieranie nieznanego sygnałuJeśli kierunek przychodzącego sygnału nie jest znany (a jeśli kanał komunikacyjny jest wielokierunkowy, kierunków może być kilka), analizując odbierany sygnał przez siatkę antenową, można utworzyć optymalny wektor wag. w W ten sposób OSW na wyjściu bloku przetwarzania przestrzennego będzie maksymalne.
Optymalne odbieranie sygnału w obecności zakłóceńW tym przypadku zadanie stawiane jest w następujący sposób: znane są parametry przestrzenne oczekiwanego użytecznego sygnału, jednak w otoczeniu znajdują się źródła zakłóceń. Należy maksymalizować OSZW na wyjściu AR, jednocześnie minimalizując wpływ zakłóceń na odbiór sygnału.
Optymalne przekazywanie sygnału do użytkownikaZadanie to jest rozwiązywane w systemach komunikacji mobilnej (4G, 5G) oraz w Wi-Fi. Chodzi o to, że z wykorzystaniem specjalnych sygnałów pilotowych w kanale zwrotnym użytkownika ocenia się parametry przestrzenne kanału komunikacyjnego, na podstawie których wybiera się optymalny wektor wag do transmisji.
Przestrzenne multiplexing danychAdaptacyjne siatki antenowe umożliwiają przesyłanie danych do kilku użytkowników jednocześnie na tej samej częstotliwości, tworząc dla każdego z nich indywidualną charakterystykę kierunkową. Ta technologia nazywa się MU-MIMO i obecnie jest aktywnie wdrażana (gdzieś już) w systemach komunikacyjnych. Możliwość przestrzennego multiplexingu jest przewidziana na przykład w standardzie komunikacji mobilnej 4G LTE, standardzie Wi-Fi IEEE802.11ay oraz w standardach 5G.
Wirtualne siatki antenowe dla radarówCyfrowe siatki antenowe pozwalają za pomocą kilku elementów antenowych utworzyć wirtualną siatkę antenową o znacznie większych rozmiarach do przetwarzania sygnału. Wirtualna siatka ma wszystkie cechy rzeczywistej, jednak wymaga mniejszych nakładów sprzętowych.
Ocena parametrów źródeł promieniowaniaAdaptacyjne siatki antenowe pozwalają rozwiązać zadanie oceny liczby, mocy, źródeł promieniowania radiowego, ustalać statystyczny związek między sygnałami różnych źródeł. Główną zaletą adaptacyjnych układów antenowych w tym kontekście jest zdolność do superrozdzielczości blisko położonych źródeł promieniowania. Źródeł, kątowa odległość między którymi jest mniejsza niż szerokość głównego płata diagramu kierunkowości układu antenowego (). Przede wszystkim staje się to możliwe dzięki wektorowej reprezentacji sygnału, znanemu modelowi sygnału oraz narzędziom matematyki liniowej.
Dziękuję za uwagę
Źródło: habr.com
