Підводний «GPS» на два прийомопередавача

Привіт дорогий читач!

Нашому підводному GPS днями виповнилося три роки. За цей час система стала серійною, ми вивели на ринок ще кілька систем і пристроїв, але весь цей час мене не покидала нав’язлива ідея принести гідроакустику в широкі маси, зробити її доступною простим любителям, моделістам, представникам такого напрямку, як міські божевільні citizen science та іншим зацікавленим.
І сьогодні, в честь нашого скромного ювілею, я докладно і на пальцях поясню, як визначати географічне положення підводного об’єкта за допомогою всього двох приймачів: з вихідним кодом, веселими малюнками, графіками й результатами експериментів.
Всіх зацікавлених прошу до нас на байдарку, в чисті і теплі води Волгоградського Водосховища!

УВАГА: стаття містить залишкові кількості матана!

Для тих, хто не розуміє, що відбувається, але не проти вникнути, пропоную ознайомитися з нашими попередніми публікаціями по темі гідроакустики та підводної навігації:
Сеанс передачі відео звуком через воду .

Ввідні

Під водою радіохвилі не поширюються на скільки-небудь значиме відстань, і більш-менш пристойна бездротовий зв’язок і навігація там, поклавши руку на серце, можлива тільки за допомогою акустики.
Є всього два з половиною працюючих типу навігаційних систем:

  • ультракороткобазисні (УКБ, USBL) – засновані на визначенні кута приходу сигналу маяка і вимірювання дистанції (або методом запит-відповідь або за синхронізованим часом). Кут приходу визначається за допомогою антенної решітки. Шукане положення визначається за кута і дальності (пряма геодезична задача). УКБ системою наприклад, є наша ZIMA
  • длиннобазисні (ДБ, LBL) – де вимірюються часи приходу сигналу або на декілька приймачів, або від декількох передавачів (наш підводний GPS працює саме так) з відомим положенням. Можна або вимірювати дистанції за методом «запит-відповідь» (дальномерный спосіб) або вимірювати різниці часу приходу сигналу (різницево-дальномерный спосіб, так, наприклад, працюють GPS і GLONASS. Довга база буває плавуча (як наша RedWAVE) або донна.
  • Короткобазисні (КБ, SB) – по суті — ті ж довгобазисні системи, але всі опорні елементи бази розташовуються на одній підставі – приміром, на судні.

Як я вже сказав, довга база може працювати за принципом «запит-відповідь», в цьому випадку потрібно вирішити задачу про перетин N сфер, або кіл, якщо відома глибина шуканого об’єкта.
А може база працюватиме так само як GPS і GLONASS, такі системи ще називають гіперболічними, і ось чому: якщо шуканий об’єкт періодично випромінює навігаційний сигнал, а годинники всіх приймачів (елементів бази) синхронізовані, то дистанцію безпосередньо виміряти не можна, а можна тільки дізнатися до якого з елементів бази джерело ближче або далі. Тобто в наявності тільки різниці дистанцій. А як відомо з шкільного курсу:
«Гіпербола може бути визначена як геометричне місце точок, абсолютна величина різниці відстаней від яких до двох заданих точок, що називаються фокусами, постійна»
Іншими словами, якщо підводний об’єкт випромінював сигнал, і ми взяли його на двох приймачах, час яких синхронізований, то ми можемо стверджувати, що наш об’єкт лежить на гіперболоїді (або на гіперболі, якщо він раптом передав нам свою глибину). Більше приймачів – більше гіпербол! Більше гіпербол – вище точність визначення місцеположення немає площі – ні площини.
А якщо випромінює не підводний об’єкт, а елементи бази, то підводний об’єкт, знаючи свою глибину, сам може визначити своє місце розташування, і більше того, він у цьому випадку нікому не заважає і таких об’єктів може бути скільки завгодно – вони всі просто слухачі. Так влаштовані GPS/GLONASS і наш RedWAVE.
А я обіцяв навігацію по двом прийомопередавачам, один з яких – той, чию позицію як раз і треба визначити.

Давайте розбиратися

Відстань між двома точками і в тривимірному просторі визначається (хіба мало, раптом хто забув) за простою формулою:

Нехай у нас є N базових станцій. З гуманних міркувань припустимо, що їх координати відомі, і ми виміряли відстані від кожної з базових станцій до мети T. Для ясності: T – це target, a B – base.
Стало бути, ми можемо для будь-якої точки записати вираз, який показує, наскільки ця будь-яка точка підходить під наші експериментальні вимірювання. Фактично це буде сума різниць між відстанню від точки М до відповідної базової станції і виміряною дистанцією. Кожну цю різницю зведемо в квадрат.

Загалом, справа йде до методу найменших квадратів, а координати шуканої точки в ідеалі повинні бути мінімумом функції ε.
Якщо ж мова йде про різницево-дальномірний спосіб, якого ми торкнемося в цій статті зовсім огульно, і у нас кожній базові станції відповідає не виміряна дистанція , а час приходу сигналу , то у виразі функції помилки будуть не дистанції, а їх різниці. З тією лише різницею, що доведеться перебрати потрібні пари базових станцій, наприклад, для n-го і n+1-ої станції:

v – тут означає швидкість звуку, яка, до речі, не постійна, а залежить від щільності середовища, від температури, солоності і тиску. І якщо хочеться зробити все по-дорослому, то її потрібно або безпосередньо вимірювати, або розраховувати, але про це як-небудь іншим разом.
Зазначимо, що глибина розташування об’єктів в сучасних гідроакустичних навігаційних системах вимірюється безпосередньо і з досить високою точністю (0.5-2 см), отже, завдання має сенс перевести в плоску.
Можна було б повідомити, що тепер ось це вирішуємо будь-яким методом оптимізації і на цьому закінчити. Але, по-перше, не все так просто, а по-друге, не все так складно.

Почнемо зі складностей

Справа в тому, що дуже багато залежить від розміщення базових станцій відносно один одного (на це ще якось можна впливати, наприклад, адміністративними методами =) і положення позиціонуємого об’єкта, щодо елементів бази (на це впливати набагато складніше, у широкому колі завдань спочатку положення позиціонуємого об’єкта відомо дуже приблизно і заздалегідь вигідно встановити базу майже неможливо).
Отримати ситуацію як на малюнку 1 – як з добрим ранком:

Читайте також  Як вибрати мову програмування для створення Андроїд — додатку


Малюнок 1 — Поверхня помилок для бази з трьох елементів. Позиціонування об’єкту поза бази. Наявність помилкового мінімуму

Зеленими колами позначені положення базових станцій, а зірочкою – істинне положення позиціонуємого об’єкта. Колір точок показує величину функції нев’язки (ε) в цій точці. Як казав один політик, абсолютно зрозуміло, що поверхня має дві западини, і при пошуку мінімуму дуже легко потрапити в помилкову. Ситуація виявляється ще гірше: беручи до уваги масштаб, реальний стан об’єкта і середню швидкість руху таких об’єктів можна сказати, що в такому положенні він буде знаходиться досить довго, форма поверхні помилок буде зберігатися, і дуже велика ймовірність тривалий час отримувати неправильні дані про місце розташування об’єкта.
На анімації нижче ілюструється подібна ситуація – і при старті пошуку з середньої точки між базовими станціями, і при старті від ближньої станції пошук веде до хибного мінімуму:


Малюнок 2 — Потрапляння пошуку в помилковий мінімум при старті з середньої точки бази. База з трьох елементів

Пошук тут реалізований за допомогою алгоритму Нелдера-Міда, або як його ще називають – методу симплексу. Саме він використовується, наприклад, в Matlab функції fminsearch
Він не вимагає обчислення похідних, дуже наочний, інтуїтивно зрозумілий і простий у реалізації.


Малюнок 3 — Потрапляння пошуку помилковий мінімум при старті з найближчого елемента бази. База з трьох елементів

Знаючі люди можуть обуритися, що базові станції в лінію ніхто не ставить, але, по-перше, іноді все-таки ставлять, а по-друге — це перебільшення для більш наочної демонстрації.
«Поразка була повною. Придумати що-небудь не представлялося можливим» (С) А. і Б. Стругацькі, «Град Приречений»

Що ж тут можна придумати?

Логіка підказує, що можна скористатися одним з двох методів глобальної оптимізації – Simulated Annealing або чимось подібним. Однак, інший (і більш продуктивний) підхід полягає в більш повному використанні апріорної інформації для вибору початкової точки пошуку.
Адже ми точно знаємо, що шукана точка лежить (з часткою допущення) на окружностях, центри яких знаходяться базові станції, а радіуси цих кіл відповідають виміряним дальностям!
А ще ми впевнені в тому, помилка вимірювання дальності в гідроакустиці зростає разом з дальністю: звук поширюється нелінійно.
Можна взяти і спробувати вибрати початкову точку пошуку мінімуму навколо (буквально, на окружності) найближчого маяка, розраховуючи, що дальність до нього виміряна з максимальною точністю, і керуючись тим же самим значенням функції нев’язки ε.
Координати точок-кандидатів формуються в цьому випадку згідно з наступним простим формулами:

і – координати ближньої (c – closest) базової станції, – відстань від неї до позиціонуємого об’єкта. – аргумент (кут). Якщо тепер пошукати мінімум для , варіюючи в діапазоні від 0 до 360 градусів з кроком, наприклад, в 10 градусів, а потім пошукати поряд з цим значенням зменшивши крок до 1 градуса і звузивши область пошуку до 20 (± 10) градусів, то можна дуже добре визначити початкове наближення, яке практично у всіх випадках відразу б’є майже точно в ціль!
На картинках нижче метод рішення «в лоб» за 30 ітерацій відважно потрапив у помилковий мінімум, що призвело до абсолютно неправильним рішенням, а метод з попередньою одномірної мінімізації майже відразу потрапив в точку і за 6 ітерацій наблизився до дійсному стану з розбіжністю ~25 сантиметрів при відстані до найближчої базової станції ~400 метрів.


Малюнок 4 — Потрапляння пошуку помилковий мінімум


Малюнок 5 — Знаходження вірного рішення при старті з точки, отриманої попередньою одномірною мінімізацією


Малюнок 6 — Збільшена область навколо отриманого рішення. Жовтий круг — отримане рішення, зелена зірочка — справжній стан об’єкта

У вищенаведеному прикладі вже перша ітерація після попереднього вибору початкової позиції пошуку дала рішення в 20 метрах від реального положення (на полі ~600×600 метрів).
На даному етапі ми розібралися з далекомірним способом, з різницево-далекомірним ситуація трохи складніше: там немає таких чітких орієнтирів для вибору початкової точки (коло – гарна фігура, тому що він замкнутий, а гіпербола – погана, тому що йде на нескінченність).
Повернемося до обіцяного “підводного GPS” з двох приймачів. У якомусь сенсі я звичайно злукавив: повноцінний підводний GPS як з нашої першої статті не вийде, але якщо немає грошей купити правильний дівайс змиритися з деякими обмеженнями і особливо зупинитися на певному колі завдань, то зробити дещо все-таки можна.
Часто позиціонується об’єкт нерухомий (ну або хоча б який рухається дуже повільно). В таких випадках замість декількох базових станцій можна використовувати одну, але переміщувану: такий підхід називають VLBL або віртуальна довга база. Якщо нам не потрібно фіксувати трек руху підводного об’єкта, а просто визначити його місцезнаходження – це те, що потрібно.
Часто така задача виникає при встановленні якогось донного обладнання шляхом вільного скидання з судна. При відчутних глибинах і наявності течій реальний стан обладнання на дні може значно відрізнятися від положення точки, де його скинули. Цим обладнанням може бути і елемент донної навігаційної бази: щоб визначити місце розташування підводного об’єкта, спочатку потрібно визначити місце розташування підводного об’єкта.

Отже, як все це може бути влаштовано в реальному світі?

Наш експериментальний сет складається з:

  • об’єкта, який ми будемо ховати на дні водойми: наш кодовий модем RedGTR в автономному виконанні (це тільки звучить офіційно, на ділі – модем, підключений до герметичного батарейного боксу); Ось він на фото:

    Малюнок 7 — Маяк-відповідач з батарейною каністрою
  • рухома базова станція на базі приманки китайських човнів. У неї на борту, крім рідної начинки управління, встановлений другий модем RedGTR, радіомодуль DORJI на 433 МГц з антеною, саморобний модуль GPS/GLONASS на базі quectel l76, власна плата на базі STM32F429 (може бути легко замінена на NUCLEO-STM429 або STM32F4 Discovery, впевнений, що і будь-яка Arduino-подібна плата теж впоралася б) яка просто грає роль маршрутизатора: з радіоканалу по UART все йде в модем і навпаки, дані з GPS-модуля також сипляться в радіоканал.
    Так це виглядає в зборі:

    Малюнок 8 — Фото тестового плавзасоба в зборіНа цей раз ми приколхозили якусь подобу транцем для кріплення штанги з модемом, досвід вільно буксованого навігатора виявив хибність такої схеми як з точки зору керованості, так і з точки зору мінливого взаємного розташування пристрою і човна.
    Так все виглядає в трюмі:

    Малюнок 9 — Фото тестового плавзасоба. Вид в трюм

    Всі рідні схеми залишилися майже без змін, а все додаткове обладнання живиться від окремого свинцевого АКБ на 1.2 А*ч.

  • пульта оператора, до складу якого входить ноутбук з ПО і відповідь радіомодуль, що підключається через перетворювач до USB. ЗА звичайно дещо поспішне і годиться тільки для демонстрації, але там вже реалізований протокол взаємодії з модемом, TOA-вирішувач, на основі того ж самого методу Нелдера-Міда, вибір базових точок, проста демонстрація географічних позицій а також можливість емуляції GPS для передачі даних в якийсь аналог Google Earth.

    Малюнок 10 — «Пульт оператора». Тестовий прогін на столі.Сині точки — положення човна по GPS, зелені — положення, в яких були зроблені виміри, лососевим виділені положення елементів віртуальної бази, червоним — обчислені положення маяка-відповідача.

 

Хто чим зайнятий?

 

  • модем, який потрібно знайти, просто лежить на дні, веде прийом і відповідає на запити;
  • модем, який на човні, виконує команди, що прийшли по UART – посилає запити і приймає відповіді, про що повідомляє UART
  • плата з STM32F4 в човні отримує дані з GPS і модему по UART і перенаправляє їх в радіомодуль по іншому UARTу, а дані з радіомодуля вона перенаправляє в модем; Тут є невеликий нюанс, пов’язаний з радіомодулем — він не повнодуплексний (прям як в гидроакустиці =)), тому плата «притримує» повідомлення RMC від GPS-модуля і відсилає його тільки разом з повідомленням від модему;
  • пультове ЗА керує всією дією, збирає дані про відстані до цілі з різних географічних положень, вибирає оптимальну базу та визначає місце розташування цілі;

 

Трохи більш детально

Коммунікація з модемами відбувається з NMEA-подібного протоколу. Для нашої задачі використовується всього три повідомлення:

$PTNTE,targetAddr,requestedCmd,timeoutMs*hh

від пульта до модему. Передати віддаленому абоненту за адресою targetAddr команду requestedCmd, таймаут очікування відповіді встановленим рівним timeoutMs.
Команда може бути будь-яка із затвердженого списку:

КомандаКодОпис

CDS_CMD_PING Запит PING на який абонент відповідає PONG
CDS_CMD_PONG 1 Відповідь на запит PING
CDS_CMD_DPT 2 Команда віддаленого клієнта  на передачу його глибини
CDS_CMD_TMP 3 Команда віддаленого клієнта на передачу його температури
CDS_CMD_BAT 4 Команда віддаленого колієнта на передачу напруги його живлення
CDS_CMD_USR_0 5 Користувацька команда 1
CDS_CMD_USR_1 6 Користувацька команда 2
.. . .. . .. .
CDS_CMD_USR_34 39 Користувацька команда 34

Модем підтримує базові функції: пінг, передачу глибини, температури води, заряду батареї і набір користувальницьких кодових команд.
На команду TNTE модем відразу відповідає про факт прийняття команди або неможливості її виконати повідомленням АСК:

$PTNT0,errCode

errCode – 0 якщо модем прийняв команду, в іншому випадку код помилки
Якщо модем не дочекався відповіді віддаленого абонента вчасно, він повідомляє про це повідомленням REM_TIMEOUT, що містить у полі параметрів тільки адресу віддаленого абонента:

$PTNTB,targetAddr

Якщо модем прийняв відповідь вчасно, то він повідомляє про це командою REM_PONGEX, яка має такий формат:

$PTNTD,requestedAddr,requestedCmd,receivedValue_decoded,snrd,dpl,pTime,[dst],[dpt],[tmp]

requestedAddr – адресу потрібного абонента
requestedCmd – запрошена команда
receivedValue_decoded – прийняте значення
snrd – співвідношення сигнал-шум на виході приймача в дБ
dpl – доплерівський зсув в Гц
pTime – час поширення сигналу (в одну сторону) в секундах
dst – дистанція до абонента в метрах
dpt – власна глибина модему в метрах
tmp – температура забортної води в градусах Цельсія

Останні три параметри передаються, тільки якщо модем у виконанні з вбудованим датчиком тиску/температури (наш варіант).
Від модуля GPS нас цікавить тільки повідомлення RMC, звідки ми беремо поточну географічну позицію (можна було б використовувати і GGA або GLL — останнє найкоротше).

Найскладніше – передачу даних через воду і вимірювання дистанції – виконують модеми, визначення географічної позиції човники – GPS модуль, залишилася сама малість – правильно скористатися цими даними.
Весь распарс і побудова повідомлень NMEA виконуються за допомогою класу NMEAParser в бібліотеці UCNLNMEA.
Пультова, що працює на ноутбуці, періодично посилає запити REM_PINGEX абоненту за вказаною в настройках програми адресою, робить вона це за фактом отримання REM_TIMEOUT або REM_PONGEX. Попутно фіксуються координати човники і зберігаються дані про дистанції і глибині віддаленого абонента разом з координатами човники, в яких вони були отримані.
Здоровий глузд (і досвід) підказує нам, що для визначення позиції підводного об’єкта потрібно 3-5 віртуальних базових станцій, і як видно з картинок вище, розташовані вони повинні бути не аби як.
Тобто формується наступна завдання: по мірі надходження вимірювань необхідно певним чином вибирати кілька з них в якості елементів навігаційної бази.

Пропоную наступний варіант: вибирати такі вимірювання, щоб вони утворювали як можна більш опуклу фігуру, описану навколо передбачуваного місця розташування позиціонуємого об’єкта.
Для цього візьмемо деяку точку (на початковому етапі, коли зовсім незрозуміло де позиціонується об’єкт, підійде середня точка всіх вимірювань, яка потім буде замінена на перше наближення позиції шуканого об’єкта), і порахуємо від неї азимутальний кут (кут між напрямком на північ і напрямком на точку) на кожну точку вимірювань. І тепер виберемо кілька таких, які б найбільш рівномірно покривали отриманий діапазон кутів. У пультові ЗА це робить метод

List<Measurement> Measurements.GetBase();

По мірі надходження даних, у методі де парс повідомлення REM_PONGEX додаємо вимірювання і намагаємося побудувати навігаційну базу і вирішити з допомогою неї навігаційну задачу. За це відповідає метод

GeoPoint3DWE LocateLBL_NLM(List<Measurements> base, GeoPoint3DWE prevLocation, double rErrorThreshold, out double stStageRErr, out int itCnt);

Йому передається власне база – набір вимірювань, які ми відібрали для вирішення навігаційної задачі, попереднє положення (якщо воно є, там же передається і глибина шуканого об’єкта), поріг радіальної помилки, і два вихідних діагностичних параметра – радіальна помилка на першій стадії вирішення (за одномірної оптимізації) і число ітерацій, після якого було отримано рішення на другій стадії.
Якщо отримане рішення нас влаштовує по радіальній помилці, то це рішення ми будемо використовувати в якості опорної точки для побудови бази.
І так поки не набридне буде отримано задовольняє нас рішення.

Метод LocateLBL_NLM, як може бути зрозуміло з назви, вирішує задачу методом Нелдера-Міда, попередньо виконуючи одновимірну оптимізацію, якщо немає попереднього рішення. В реальності можна і не прив’язуватися до попереднього результату, а застосовувати одновимірну оптимізацію постійно – стратегія може бути різною і залежить від конкретних умов.

Для простоти, що рішення виконується в метрах, для чого перед початком рішення всі координати переводяться в локальну систему, з початком відліку в середній точці навігаційної бази. Після вирішення результат назад переводиться в географічні довготу і широту (довгота – це Х, а широта – Y). Для перекладу градусів метри і тому клас Navigation містить методи GetDistance2DDeg і Meters2Deg.
Але, як відомо, теорія без практики мертва, і пора перейти до результатами натурних експериментів.

Оживимо теорію практичною перевіркою

За старою традицією, випробування проводимо в гирлі річки Пичуга. Там зручне місце, є глибини майже до 30 метрів, і досить складна акваторія.
Маяк-відповідач закріплений на спеціальному черешку «стенді» (в кадр не потрапили пінопластові балберки і якір, які допомагають всієї конструкції триматися вертикально):

Малюнок 11 — «Донна станція» маяк-відповідач.

Встановлення маяка зняли на відео, трохи не розрахували зі світлом і забули взяти з собою ліхтар, так що момент контакту з грунтом залишився прихованим у темряві.
Поки збиралися на випробування дали човнику ім’я:


Малюнок 12 — “Палич” в природному середовищі

На транец “Палича” нейлоновими стяжками закріплений шматок від вудки, на якому і розташовується модем RedGTR (щоб не бовтався і не спливав).
Експеримент полягав у тому, що маяк-відповідач встановлюється на дно, зверху від нього стирчить тільки невеликий поплавок, “Палич” при цьому, керований з берега, ходить по акваторії великими колами, пультове ПО періодично запитує у відповідача що-небудь, наприклад його глибину. Всі дані записуються в журнал, а на екрані відображається згідно реальному стану речей. Ось так:


Малюнок 13 — Скріншот пультового з траєкторією човни і обчисленими положеннями маяка-відповідача (червоним показано краще обчислене положення)

Як видно з малюнка 13, маяк розташовувався на глибині 13.2 м, а температура води на цій глибині склала 24.1 °С.


Малюнок 14 — Імпорт отриманих треків в GoogleEarth

Отримання хорошого результату в даному випадку зайняло від сили хвилин 15 плавання.
У другому експерименті маяк був встановлений на глибині 16.5 метрів, де очікувано кілька холодніше — всього 22.6 °С.


Малюнок 15 — результат визначення місця розташування маяка-відповідача у другому експерименті на екрані пультового ЗА

Правильною оцінкою точності в даному випадку ми не займалися зважаючи на гостру нестачу часу (насправді дані потішні випробування стали причиною деяких непорозумінь серед керівництва, бо відривали від роботи), а вдовольнилися тим, що коли судно виходило до поплавка (цей момент можна бачити на першому треку), то в плані його позиція повністю співпадала з кращим рішенням, а дистанція збігалася з глибиною (з різницею глибин відповідача і модему, встановленого на судні). Точки, в яких проводився скидання маяків, фіксувалися, і розбіжність між обчисленим положенням і точками скидання знаходиться в районі 2-3 метрів, що додатково підтверджує працездатність системи.

У загальному і цілому, можна констатувати що даний proof-of-concept успішно виконаний!
Ми отримали справжню насолоду і сонячні опіки, будемо раді відповісти на питання і вислухати критику!

Післямова

Сумний факт полягає в тому, що застосовувані в даному експерименті модеми хоч і дуже дешеві по мірках світового ринку засобів гідроакустичної зв’язку, все ж не дуже щоб доступні любителям.
Щоб не бути голослівним, наведу посилання на відмінну роботу міс Бенсон, в якій на сторінці 54 з PDF або 36 по нумерації документів є цікава табличка з цінами на деякі гідроакустичні модеми станом на 2010 рік. Такі справи. (В таблиці є невелика помилка — дальність зв’язку Range для розроблюваного нею модему вказана в метрах, в той час як в заголовку колонки варто km).
Гарна новина полягає в тому, що ми раптово розробили модеми, які можуть майже все теж саме (трохи менше за розмірами (за фактом — найменші в світі, а до них найменші були теж наші ), менше дальність зв’язку, нижче швидкість передачі), і за нашими оцінками стоять вже цілком підйомно для заможних ентузіастів. На даний момент електроніка та прошивка повністю готові, а ми допилюємо корпусне рішення. По закінченню розробки обов’язково опублікуємо статтю з результатами експериментальної перевірки.

Читайте також  Котики проти нейромережі. Або вибираємо і запускаємо нейромережа для розпізнавання об'єктів на Raspberry Zero

Степан Лютий

Обожнюю технології в сучасному світі. Хоча частенько і замислююся над тим, як далеко вони нас заведуть. Не те, щоб я прям і знаюся на ядрах, пікселях, коллайдерах і інших парсеках. Просто приходжу в захват від того, що може в творчому пориві вигадати людський розум.

You may also like...

Залишити відповідь

Ваша e-mail адреса не оприлюднюватиметься. Обов’язкові поля позначені *