Приветствую, глубокоуважаемые!
Если вы не знаете, какой размер выборки вам подойдет, то можно взять число 216. О. В. Верходанов, Астрофизик
Вот уши у человека - это антенная решетка, как и у других живых существ, обладающих слухом. Формально она простенькая - всего из двух элементов, но в реальности все устроено интереснее - мозг осуществляет очень сложную многоуровневую обработку, которая в том числе связана и с ощущением габаритов своего тела, силы звука, восприятием разных частот, эффектом Доплера, не говоря уже об опыте.
Сегодня мы попытаемся не то что бы понять - нам важно немного прикоснуться к принципам, которые лежат в основе угломерных пеленгационных гидроакустических систем. Используя практический подход, конечно.
Мы будем делать угломерную систему на основе антенны из 4 (четырех) приемников. Конечно, в предельном случае хватило бы и двух, но, как мы уже упомянули, чтобы добиться приемлемого результата с двумя "ушами" нужен хотя бы рептильный мозг, а у нас нет никакого. Поэтому качество будем компенсировать количеством - это распространенная практика в природе, социальной жизни и технике.
Эта статья является продолжением предыдущей статьи Гидроакустические «кубики» - где я рассказывал о создаваемом нами наборе разных гидроакустических шилдов, из которых, по нашей задумке, можно будет сделать макет практически любой гидроакустической системы. Я постарался представить материал так, чтобы без лишней надобности не отсылать глубокоуважаемого читателя к предыдущей статье.
0. Intro
Среди акустических кубиков на данный момент мы сделали два типа. Именно на них в прошлой статье мы измеряли дальность и оценивали точность этих измерений - в тазу, в бассейне и в естественном водоеме. Вот эти устройства:
импульсный одночастотный передатчик A³T, который излучает импульсы фиксированной частоты и длительности при изменении состояния на своём цифровом входе, которым управляет пользователь
одночастотный приемник A³R, который может улавливать эти импульсы и передавать информацию пользователю, изменяя состояние своего цифрового выхода.
Эти устройства спроектированы так, что могут объединяться в стек до 12-ти штук - именно на столько рассчитана шина, примерно так:

Например, можно объединить приемник с передатчиком и они смогут работать с одной антенной, а можно объединить несколько приемников с одним передатчиком, настроить при помощи джамперов адрес на шине для каждого приемника и читать при помощи любого МК выход приемников. При этом каждый приемник будет работать со своей антенной, антенны можно пространственно разнести, и вот мы уже приближаемся к тому, чтобы сделать подводные "уши".
1. Что будем делать
Во-первых, чтобы понять, на каком приемлемом расстоянии надо расставить наши "уши" - антенны, надо определить, как сильно флуктуирует момент детектирования сигнала в разных приемниках.
Коль скоро мы будем изготавливать линейный массив - ULA (uniform linear array), расстояние между приемными элементами должно быть не меньше, чем амплитуда флуктуаций, говоря простым языком, если у нас разные приемники, антенны которых лежат рядом определяют момент прихода сигнала с разностью друг относительно друга порядка 1 миллисекунды, то расстояние между элементами менее 1.5 метров делать бессмысленно.
Во-вторых, нам нужно определиться, получится ли читать состояние пинов четырех приемников при помощи Arduino Nano с достаточной скоростью и точностью или придется поискать какую-то более производительную платформу.
Теперь определимся с набором оборудования.
№ | Наименование | Кол-во |
1 | Модуль A³R | 4 |
2 | Модуль A³T | 1 |
3 | Антенна приемопередающая RT-1.332820-1 | 1 |
4 | Антенна приемная R-1.d3505-1 | 4 |
5 | Любая плата с МК, например, Arduino Nano | 2 |
6 | Провода Dupont Male-Female или Female-Female, 15+ см | 8 |
Естественно, дополнительно потребуются два источника питания, кабель для прошивки и получения данных от Arduino.
В ходе этого проекта нам предстоит сделать приемную и передающую части, причем, приемная будет ощутимо сложнее, а передающая очень простой. Передающая необходима для проверки и отладки приемной, поэтому начнем именно с нее.
2. Пингер
Пожалуй, это будет самое простое устройство из тех, что мы сделаем на протяжении этого курса. Вся его задача состоит в том, чтобы раз в некоторый временной период инициировать передачу. Соединение платы передатчика с платой Arduino Nano сделаем согласно таблице:
Номер/Наименование контакта на XS2 | Номер/Наименование контакта на Arduino Nano |
1 / GND | GND |
4 / Инициация передачи импульса | 10 |
20 / VCC | Vin |
ОЧЕНЬ ВАЖНО! В таблице выше указано, что выход напряжения питания с платы предатчика заводится на пин Vin платы Arduino Nano - это делается только после того, как плата будет прошита и отключена от ПК!!! В противном случае, она скорее всего выйдет из строя!
К плате передатчика, также, не забываем подключить приемопередающую антенну. Вот простейший скетч для передатчика:
Скетч передатчика
#define A3T_TX_ENGAGE_PIN (10) #define LED_PIN (13) #define PING_HALF_PERIOD_MS (2000L) #define TX_STROBE_DURATION_MS (10L) void setup() { pinMode(LED_PIN, OUTPUT); digitalWrite(LED_PIN, LOW); pinMode(A3T_TX_ENGAGE_PIN, OUTPUT); digitalWrite(A3T_TX_ENGAGE_PIN, HIGH); } void loop() { delay(PING_HALF_PERIOD_MS); digitalWrite(A3T_TX_ENGAGE_PIN, LOW); digitalWrite(LED_PIN, HIGH); delay(TX_STROBE_DURATION_MS); digitalWrite(A3T_TX_ENGAGE_PIN, HIGH); delay(PING_HALF_PERIOD_MS); digitalWrite(LED_PIN, LOW); }
Его единственная задача состоит в том, чтобы через заданный промежуток времени притягивать ножку, управляющую передатчиком на 10 миллисекунд к земле, инициируя тем самым передачу сигнала.
С передатчиком все, выглядеть он должен вот так:

3. Приемник
Четыре платы приемника мы объединим в стопку - сделаем то, для чего они и были предназначены. Но перед этим нужно подключить антенны и задать адреса, чтобы сигналы от разных приемников были разнесены по шине.

Рекомендуем установить платы в стопку в прямом или обратном порядке: чтобы снизу был адрес 1, выше 2, 3 и приемник, с адресом 4 на самом верху - так будет проще избежать путаницы с антеннами.
Антенны тоже крайне рекомендуется пронумеровать, можно повесить бирки на кабели или прямо написать на антенне номер восковым карандашом (в воде он держится, пока не потереть пальцами). Ни в коем случае не стоит использовать перманентный маркер - при длительном контакте краситель может диффундировать в полимер и стереть его будет крайне затруднительно.
Далее подключаем стопку приемников к плате Arduino Nano согласно таблице:
Номер/Наименование контакта на XS2 | Номер/Наименование контакта на Arduino Nano |
|---|---|
1 / GND | GND |
6 / Строб приемника №1 | 2 |
8 / Строб приемника №2 | 3 |
10 / Строб приемника №3 | 4 |
12 / Строб приемника №4 | 5 |
Наш приемник я уже показывал на одной из картинок выше, но приведу еще раз чтоб не листать:

3.1. Скетч для обработки антенной решетки
По задумке, скетч должен передавать на ПК четыре времени прихода сигнала на каждый из приемников. Важно отслеживать разные ложные срабатывания и основным критерием того, что мы принимаем наш сигнал будет тот факт, что все времена прихода находятся в некотором диапазоне, определяемом размерами нашей антенной решетки.
Скетч для обработки антенной решетки
#include "Limits.h" #define A3R1_STATE_PIN (2) #define A3R2_STATE_PIN (3) #define A3R3_STATE_PIN (4) #define A3R4_STATE_PIN (5) #define LED_PIN (13) const uint8_t inputPinsMasks[] = { B00100000, B00010000, B00001000, B00000100 }; const uint8_t inputPins[] = { A3R1_STATE_PIN, A3R2_STATE_PIN, A3R3_STATE_PIN, A3R4_STATE_PIN }; const int numPins = 4; bool lastPinState[numPins]; bool pinFallen[numPins]; unsigned long fallTime[numPins]; const unsigned long DETECTION_WINDOW = 2000; bool is_any_pin = false; unsigned long pin_minTime = 0; unsigned long pin_maxTime = 0; uint8_t fallen_pins = 0; void checkPinTimes() { is_any_pin = false; fallen_pins = 0; for (int i = 0; i < numPins; i++) { if (pinFallen[i]) { fallen_pins++; if (!is_any_pin) { is_any_pin = true; pin_minTime = fallTime[i]; pin_maxTime = pin_minTime; } } } if (is_any_pin) { for (int i = 0; i < numPins; i++) { if (pinFallen[i]) { if (fallTime[i] > pin_maxTime) pin_maxTime = fallTime[i]; if (fallTime[i] < pin_minTime) pin_minTime = fallTime[i]; } } } } void resetAllFlags() { for (int i = 0; i < numPins; i++) { pinFallen[i] = false; } } void setup() { Serial.begin(9600); for (int i = 0; i < numPins; i++) { pinMode(inputPins[i], INPUT_PULLUP); lastPinState[i] = (PIND & inputPinsMasks[i]) > 0; pinFallen[i] = false; fallTime[i] = 0; } } void loop() { unsigned long currentTime = micros(); uint8_t cPIND = PIND; for (int i = 0; i < numPins; i++) { bool currentState = (cPIND & inputPinsMasks[i]) > 0; if (!currentState && lastPinState[i]) { pinFallen[i] = true; fallTime[i] = currentTime; } lastPinState[i] = currentState; } checkPinTimes(); if (is_any_pin) { if (fallen_pins == numPins) { if ((pin_maxTime - pin_minTime) <= DETECTION_WINDOW) { for (int i = 0; i < numPins; i++) { Serial.print(fallTime[i] - pin_minTime); Serial.print(", "); } Serial.println(); delay(500); } resetAllFlags(); } else { if (currentTime - pin_minTime > DETECTION_WINDOW * 100) { resetAllFlags(); delay(100); } } } }
Забегая вперед, отметим, что в скетче пришлось отказаться от стандартных функций digitalRead и перейти к прямой работе с регистрами для ускорения. Это некоторым образом делает скетч менее гибким.
4. Эксперимент по определению амплитуды разброса времени прихода
Как и говорилось ранее, прежде чем делать антенную решетку, нужно определить, на сколько флуктуирует момент времени прихода сигнала на разные приемники. Для этого нам потребуется пол-ведра воды. В ведре мы можем расположить все приемные антенны в непосредственной близости от передающей. Так мы исключим все эффекты связанные с временем распространения и сможем оценить, насколько по-разному работают приемники.
Напомним, что нам это нужно для того, чтобы определиться с размерами антенной решетки.
Экспериментальная установка в нашем случае выглядит вот так:

Итак:
плата Arduino передатчика прошита и отключена от ПК
плата Arduino приемника прошита и подключена к ПК
в среде Arduino IDE открыта утилита 'Serial Monitor' или запущено любое другое терминальное приложения для получения данных с последовательного порта
на стопку приемников подано питание
на передатчик подано питание
Если все собрано правильно, то в окне утилиты 'Serial Monitor' в Arduino IDE мы сможем увидеть приходящие раз в 4 секунды строчки, содержащие по 4 числа через запятую. Скетч нормализует времена прихода - вычитает наименьшее из каждой группы. Достаточно будет набрать несколько десятков строк.
У нас получился такой набор данных
Формат CSV (Comma-separated values), нормализованные времена прихода сигнала в микросекундах #id,receiver_4,receiver_3,receiver_2,receiver1 1,0,140,56,200 2,56,236,0,56 3,0,84,24,172 4,0,28,28,180 5,0,132,132,132 6,0,172,0,172 7,0,248,160,160 8,0,228,168,24 9,0,112,52,140 10,56,316,0,168 11,0,356,52,84 12,0,204,112,0 13,0,168,80,108 14,0,272,212,212 15,0,32,0,32 16,0,264,28,112 17,0,116,52,52 18,28,168,140,0 19,0,116,56,56 20,0,92,0,32 21,0,144,28,84 22,0,252,80,192 23,0,28,60,28 24,208,244,0,208 25,60,0,28,148 26,84,0,24,112 27,132,280,188,0 28,0,136,108,168 29,168,136,0,52 30,80,136,0,136 31,136,136,0,52 32,88,204,172,0 33,0,140,228,196 34,108,108,196,0 35,28,172,140,0 36,28,116,0,56 37,120,120,0,0 38,80,228,0,164 39,164,164,0,52 40,0,104,188,188 41,0,260,108,80 42,52,152,52,0 43,0,56,28,56 44,108,0,136,136 45,28,268,120,0 46,160,248,0,188 47,0,224,76,164 48,0,280,128,128 49,0,28,56,152 50,28,84,0,204 51,0,172,52,84 52,0,224,80,192 53,80,224,0,192 54,0,140,28,140 55,0,136,52,196 56,24,168,140,0 57,88,56,28,0 58,0,92,0,56 59,0,28,184,56 60,28,84,84,0 61,0,172,80,52 62,28,148,0,88 63,80,108,140,0 64,28,152,0,64 65,28,0,60,0 66,56,96,28,0 67,172,52,0,52 68,0,224,164,52 69,0,28,88,56 70,136,108,0,168 71,0,32,0,32 72,108,228,52,0 73,0,32,0,60 74,0,276,132,244 75,84,0,260,52 76,28,88,0,28 77,0,116,28,84 78,0,340,160,132 79,0,108,108,108 80,0,164,52,164 81,28,204,0,84 82,28,120,28,0 83,0,84,56,116 84,0,104,160,192 85,28,228,0,168 86,0,232,0,144 87,0,228,76,76 88,108,0,192,192 89,0,168,112,200 90,28,112,0,144 91,56,196,0,196 92,80,0,52,120 93,56,300,84,0 94,0,336,216,132 95,0,144,28,112 96,0,148,0,176 97,112,172,28,0 98,56,0,84,116 99,0,0,0,64 100,0,164,224,80 101,0,120,0,88 102,28,60,28,0 103,108,108,0,108 104,0,80,164,164 105,112,172,0,56 106,0,28,60,28 107,28,168,200,0 108,52,0,84,52 109,24,0,112,232 110,0,260,108,52 111,0,204,140,28 112,0,348,0,168 113,0,292,60,172 114,0,220,80,220 115,28,260,0,168 116,0,132,104,164 117,116,52,0,84 118,0,216,216,132 119,0,176,0,116 120,28,144,84,0 121,52,228,0,136 122,0,172,52,108 123,84,144,0,52 124,0,152,0,0 125,0,164,196,80 126,80,228,0,164 127,0,140,140,28 128,28,176,0,84 129,56,0,28,116 130,28,28,60,0 131,0,80,80,228 132,0,88,28,184 133,60,120,0,0 134,0,120,0,32 135,120,28,28,0 136,80,196,0,164 137,0,216,184,156 138,52,224,0,196 139,0,164,80,256 140,0,80,80,80 141,84,24,0,84 142,0,176,84,56 143,108,140,80,0 144,160,248,216,0 145,32,156,0,0 146,28,208,56,0 147,104,256,104,0 148,0,280,80,192 149,28,264,84,0 150,136,0,80,256 151,0,260,112,80 152,80,232,0,80 153,56,196,0,228 154,60,180,28,0 155,0,264,84,28 156,164,344,108,0 157,0,60,0,148 158,0,60,0,148 159,28,172,264,0 160,132,0,188,220 161,56,236,24,0 162,164,108,0,164 163,64,184,32,0 164,0,164,164,52 165,0,80,136,136 166,0,228,80,140 167,56,112,0,200 168,0,140,80,52 169,28,348,0,196 170,28,60,236,0 171,136,196,0,80 172,0,32,0,156 173,0,84,84,28 174,56,0,112,204 175,28,288,0,196 176,0,132,132,164 177,28,116,0,116 178,56,152,24,0 179,0,168,80,168 180,0,52,52,180 181,156,224,0,156 182,0,112,56,172 183,84,176,0,28 184,0,116,84,28 185,0,84,28,116 186,60,0,60,28 187,0,132,164,132 188,60,120,0,60 189,0,280,132,188 190,52,172,112,0 191,88,0,28,28
Для наглядности нанесем эти времена на график:

Из этого графика мы видим, что в основном, времена прихода колеблются относительно друг друга в пределах 300-350 микросекунд. Для скорости звука ~1500 м/с это соответствует примерно 0.5 метра. Точность не феноменальная, но с этим вполне можно работать.
Очень полезно и легко запомнить, что за одну миллисекунду звук в воде распространяется примерно на 1.5 метра.
На основании этой определенной величины мы можем выбрать расстояние между элементами в нашей антенной решетке равным 1 метр. Конечно, выборка достаточно маленькая и велика вероятность, что разброс может дойти и до 1 миллисекунды, но, во-первых, мы хотим понять принцип, а во-вторых, мы сможем увидеть эти ситуации уже при вычислении углов прихода. А сейчас нам важно не усложнять себе жизнь излишне громоздкой антенной решеткой и понять, что и при этом размере, большая часть данных может иметь достаточную точность.
5. Антенная решетка
Для пуристов сразу проясним: она не фазированная. Мы просто разнесли отдельные приемные элементы на определенное расстояние.
Если у вас есть возможность каким-либо простым способом развесить 4 приемные антенны через 1 метр - очень хорошо, вам почти ничего не придется делать, у нас же такой возможности нет и мы собрали такую конструкцию из водопроводных полипропиленовых труб. Очевидный плюс такого решения в том, что это максимально просто и дешево, к тому же, если найти резъбовые пластиковые муфты, то можно сделать как мы - вся конструкция разбирается на две одинаковые половинки, чтоб ее можно было перевозить в салоне машины.

Мы предполагали, что конструкция может располагаться непосредственно на воде (речь конечно же идет о бассейне - на водоем такое носить крайне не рекомендуется) и поэтому дополнительно добавили плавучести. Но, кстати этого можно было бы и не делать.
Давайте прикинем плавучесть всей рамы. Для этого сначала взвесим ее: у нас получилось 1,5 кг. Теперь нужно прикинуть объем вытесняемой воды по нижней границе - вес у нас по верхней границе, объем - по нижней, если масса воды полученного объема с некоторым запасом превышает измеренный вес, то все в порядке: конструкция вполне будет держаться на воде. Вычислять будем очень просто, т.к. нам нужен минимальный объем, то мы просто посчитаем объем трубы, без учета фитингов - они увеличивают объем.Итак, у нас труба диаметром 25 мм, значит площадь сечения πr2=3.1415∗0.01252=0.000490859 м2. Длина всей использованной трубы 8 м, умножаем одно на другое и получаем объем вытесняемой воды: 0.000490859∗8=0.003926875 м3. Масса этого объема воды, с учетом плотности в 1000 кг/м3 составит 3.9 кг. А измеренный вес всей конструкции всего 1.5 кг, значит у нас есть 3.9-1.5=2.4 кг избыточной плавучести.
Без доработок, кабелей приемных антенн хватит чтобы свесить антенны вниз примерно на 15-20 см.

Разложим все на полу и прикинем, как это все выглядит в сборе и какой у нас есть запас по длине кабеля.

Из-за жесткости кабеля антенны могут висеть не ровно, но в нашем случае это не то чтобы совсем не влияет, но большого вклада в картину внести не должно. Тем более, что глубокоуважаемый читатель и повторитель сам вполне может решить этот механический вопрос.
6. Эксперимент в бассейне
Для всей полноты картины нам нужно набрать данных для нескольких взаимных расположений передатчика и приемной решетки.
Для начала определимся с тем, где "лево" и где "право": слева находится антенна №1 а справа - антенна №4:

Как минимум, должны быть такие расположения
"слева": передатчик находится ровно слева на линии, на которой лежат антенны
"cпереди": передатчик находится на линии, перпендикулярной линии антенны
"справа": передатчик находится ровно справа на линии, на которой лежат антенны
При возможности нужно получить данные для некоторых промежуточных расположений, например, что-то между "слева" и "спереди" и между "спереди" и "справа".
Вот наша схема эксперимента:

Для каждого взаимного расположения антенной решетки и источника рекомендуется набрать 150-200 измерений.
Часто, если вода в бассейне не отстоявшаяся, приемные и передающая антенны могут достаточно быстро обрастать пузырьками воздуха. Если в процессе проведения эксперимента вы столкнетесь с тем, что приемники внезапно перестали реагировать, при том, что все соединения в норме и нет никаких видимых причин для этого - приглядитесь внимательно к поверхности преобразователей - возможно, они покрыты пузырьками воздуха. Для восстановления работы достаточно убрать пузырьки рукой.
Полезно контролировать эксперимент - наблюдать, что данные, во-первых, приходят, а во-вторых, хотя бы в общих чертах понимать, что они адекватные.
Например, если источник строго слева, то сначала сигнал должен прийти на антенну №1, затем на №2 и так далее, а наш скетч передает времена прихода в обратном порядке, и мы ожидаем увидеть четыре подряд убывающие числа, например 1936, 1284, 724, 0,.
Крайне рекомендуем перед проведением эксперимента представить себе, какие примерно данные вы ожидаете получить. Это хорошая практика не только для этого конкретного эксперимента, но и для любого другого, и вообще хорошая практика по жизни.

Мы получили наборы данных для пяти различных взаимных расположения приемной решетки и источника сигнала: слева, между слева и спереди, спереди, между спереди и справа, справа. Тепература воды в бассейне во время проведения эксперимента была 21.6 °С.
Вот эти данные, так же - в формате CSV (comma-separated values):
Эксперимент №1 - источник строго слева
receiver_4,receiver_3,receiver_2,receiver_1 1936, 1284, 724, 0 1964, 1428, 680, 0 1924, 1424, 696, 0 1884, 1228, 728, 0 1852, 1344, 652, 0 1816, 1336, 784, 0 1936, 1228, 832, 0 1936, 1368, 724, 0 1880, 1432, 604, 0 1892, 1332, 800, 0 1980, 1392, 856, 0 1864, 1416, 808, 0 1852, 1380, 652, 0 1912, 1376, 628, 0 1924, 1476, 756, 0 1988, 1364, 756, 0 1932, 1308, 808, 0 1888, 1444, 828, 0 1972, 1348, 628, 0 1880, 1408, 628, 0 1868, 1392, 808, 0 1972, 1204, 784, 0 1872, 1400, 732, 0 1960, 1488, 652, 0 1868, 1420, 776, 0 1992, 1400, 732, 0 1928, 1392, 784, 0 1936, 1308, 784, 0 1928, 1340, 808, 0 1944, 1232, 724, 0 1908, 1344, 704, 0 1948, 1300, 576, 0 1968, 1372, 628, 0 1912, 1316, 680, 0 1936, 1224, 836, 0 1912, 1380, 656, 0 1988, 1392, 748, 0 1980, 1448, 832, 0 1852, 1344, 652, 0 1912, 1228, 724, 0 1976, 1204, 680, 0 1940, 1256, 724, 0 1976, 1528, 836, 0 1964, 1280, 808, 0 1932, 1424, 696, 0 1956, 1420, 776, 0 1968, 1340, 676, 0 1936, 1404, 596, 0 1964, 1368, 732, 0 1956, 1388, 832, 0 1968, 1404, 628, 0 1992, 1196, 776, 0 1976, 1288, 680, 0 1968, 1524, 912, 0 1872, 1336, 784, 0 1976, 1444, 884, 0 1904, 1340, 756, 0 1932, 1432, 676, 0 1980, 1444, 836, 0 1984, 1416, 780, 0 1928, 1424, 808, 0 1948, 1476, 888, 0 1900, 1420, 728, 0 1936, 1220, 832, 0 1968, 1404, 628, 0 1992, 1196, 776, 0 1976, 1288, 680, 0 1968, 1524, 912, 0 1872, 1336, 784, 0 1976, 1444, 884, 0 1904, 1340, 756, 0 1932, 1432, 676, 0 1980, 1444, 836, 0 1984, 1416, 780, 0 1928, 1424, 808, 0 1948, 1476, 888, 0 1900, 1420, 728, 0 1936, 1220, 832, 0 2000, 1196, 756, 0 1972, 1384, 604, 0 1924, 1336, 804, 0 1896, 1416, 808, 0 1972, 1528, 912, 0 1988, 1424, 732, 0 1920, 1332, 856, 0 1968, 1496, 576, 0 1840, 1420, 756, 0
Эксперимент №2 - источник между направлениями строго слева и напротив
receiver_4,receiver_3,receiver_2,receiver_1 1816, 1372, 728, 0 1640, 1852, 784, 0 1680, 1856, 732, 0 1844, 1904, 732, 0 1948, 1556, 784, 0 1760, 1792, 732, 0 1772, 1036, 724, 0 1752, 1068, 652, 0 1796, 1112, 832, 0 1760, 1232, 724, 0 1840, 1812, 388, 0 1872, 1100, 628, 0 1800, 1268, 1072, 0 1920, 1148, 704, 0 1680, 1208, 732, 0 1888, 1740, 544, 0 1640, 1816, 884, 0 1744, 1124, 732, 0 1672, 1228, 724, 0 1844, 1872, 704, 0 1976, 1384, 964, 0 1924, 1392, 776, 0 1960, 1216, 884, 0 1860, 1416, 912, 0 1844, 1340, 700, 0 1720, 1808, 884, 0 1812, 1664, 936, 0 1864, 1680, 568, 0 1764, 1856, 680, 0 1860, 1268, 652, 0 1836, 1864, 860, 0 1820, 1224, 836, 0 1808, 1336, 884, 0 1812, 1716, 884, 0 1824, 1224, 756, 0 1716, 1068, 756, 0 1796, 1668, 756, 0 1804, 1776, 884, 0 1748, 1308, 884, 0 1828, 1860, 912, 0 1880, 1344, 676, 0 1768, 1296, 624, 0 1904, 1340, 724, 0 1804, 1864, 912, 0 1892, 1324, 964, 0 1628, 1424, 836, 0 1736, 1440, 880, 0 1644, 1024, 884, 0 1908, 1224, 808, 0 1820, 1792, 596, 0 1768, 1356, 856, 0 1712, 1204, 704, 0 1904, 1256, 832, 0 1748, 1272, 884, 0 1860, 1892, 500, 0 1992, 1308, 836, 0 1852, 1940, 656, 0 1740, 1236, 704, 0 1920, 1352, 544, 0 1772, 1148, 732, 0 1968, 1260, 424, 0 1856, 1200, 756, 0
Эксперимент №3 - источник напротив
receiver_4,receiver_3,receiver_2,receiver_1 132, 0, 300, 360 104, 0, 272, 480 300, 0, 180, 388 28, 84, 0, 204 188, 0, 128, 480 0, 56, 228, 376 28, 112, 0, 112 260, 0, 260, 260 464, 28, 0, 168 688, 0, 132, 296 604, 0, 156, 156 428, 0, 28, 224 708, 0, 184, 376 640, 0, 260, 404 676, 0, 352, 184 540, 0, 208, 236 636, 0, 80, 252 560, 0, 128, 352 692, 0, 184, 184 496, 0, 52, 52 712, 0, 180, 208 652, 0, 208, 292 788, 0, 56, 256 616, 0, 156, 380 696, 0, 104, 188 736, 0, 212, 380 108, 0, 108, 168 472, 112, 56, 0 664, 0, 104, 104 172, 0, 84, 56 616, 0, 380, 208 604, 0, 80, 192 56, 24, 24, 0 612, 0, 108, 164 628, 0, 76, 272 268, 0, 184, 356 232, 0, 232, 232 528, 0, 184, 348 660, 0, 108, 300 604, 0, 160, 192 688, 0, 336, 364 612, 52, 0, 192 868, 360, 0, 360 628, 0, 104, 272 764, 0, 208, 344 664, 0, 24, 308 812, 0, 56, 192 660, 28, 0, 360 728, 0, 344, 400 564, 0, 156, 240 552, 0, 52, 220 576, 104, 0, 280 780, 0, 260, 424 572, 0, 104, 300 556, 0, 28, 196 604, 0, 160, 104 660, 0, 156, 184 644, 0, 80, 108 676, 0, 236, 264 660, 0, 80, 328 700, 0, 80, 220 680, 0, 180, 264 544, 0, 156, 212 244, 0, 188, 244 328, 0, 132, 416 312, 0, 104, 132 184, 0, 308, 248 248, 52, 0, 280 216, 0, 248, 80 192, 164, 0, 312 56, 0, 28, 296 76, 0, 272, 332 104, 0, 280, 280 136, 0, 104, 104 388, 52, 0, 388 84, 0, 28, 328 116, 28, 0, 84 164, 0, 80, 224 280, 0, 164, 220 312, 0, 136, 104 364, 0, 336, 452 352, 0, 320, 292 132, 0, 160, 220 272, 0, 104, 332 60, 0, 176, 144 296, 0, 156, 296 312, 0, 164, 104 84, 0, 116, 116 248, 0, 80, 220 104, 0, 104, 196 140, 0, 252, 340 276, 0, 80, 336 316, 0, 232, 348 168, 0, 112, 376 84, 28, 0, 232 244, 0, 160, 272 112, 0, 252, 252 52, 0, 192, 340 348, 0, 208, 408 188, 0, 300, 300 180, 0, 212, 300 220, 0, 52, 280 300, 0, 132, 300 84, 28, 0, 264 28, 0, 56, 144 272, 0, 216, 272 344, 0, 260, 344 80, 0, 136, 164 200, 0, 56, 140 288, 0, 256, 316 240, 0, 156, 328 168, 0, 112, 316 112, 0, 56, 172 264, 0, 208, 324 56, 56, 0, 264 0, 56, 0, 116 52, 0, 192, 340 56, 24, 0, 232 188, 0, 104, 248 192, 80, 0, 252 84, 0, 112, 260 372, 0, 312, 340 192, 0, 52, 224 256, 0, 112, 196 104, 0, 308, 216 324, 0, 156, 384 276, 0, 192, 276 276, 104, 0, 244 236, 0, 180, 296 168, 0, 112, 200 188, 0, 104, 336 80, 0, 164, 196 324, 0, 240, 356 160, 0, 76, 308 364, 0, 132, 216 200, 0, 60, 232 184, 0, 184, 304 304, 0, 80, 332 284, 0, 84, 224 104, 0, 76, 168 104, 0, 244, 304 296, 0, 240, 296 180, 0, 236, 356 332, 0, 80, 300 148, 0, 24, 116 188, 0, 104, 308 24, 0, 136, 228 120, 0, 28, 296 372, 0, 260, 372 232, 0, 0, 112 244, 0, 212, 212 52, 52, 0, 260 180, 0, 208, 328 292, 0, 32, 172 220, 0, 160, 104 216, 0, 160, 244 264, 0, 264, 264 0, 24, 84, 56 284, 0, 80, 192 344, 0, 372, 404 224, 0, 104, 104 112, 0, 0, 112 292, 0, 292, 472 368, 0, 284, 368 0, 56, 84, 320 308, 0, 188, 188 328, 0, 156, 240 240, 0, 180, 208 224, 52, 0, 344 168, 0, 28, 140 132, 0, 248, 188 192, 0, 160, 160 56, 28, 176, 0 132, 0, 104, 252 332, 0, 104, 392 156, 0, 156, 188 132, 0, 308, 188 160, 0, 104, 160 128, 128, 0, 220 304, 56, 0, 364
Эксперимент №4 - источник между направлениями напротив и строго справа
receiver_4,receiver_3,receiver_2,receiver_1 0, 420, 1228, 1644 0, 464, 1108, 1968 0, 364, 1060, 1680 0, 416, 1196, 1764 0, 496, 1024, 1620 0, 496, 1108, 1968 0, 128, 744, 1424 0, 604, 688, 1724 0, 364, 916, 1544 0, 652, 876, 1736 0, 104, 828, 1720 0, 292, 984, 1580 0, 388, 864, 1456 0, 596, 1208, 1924 0, 724, 864, 1460 76, 0, 748, 1308 0, 292, 1012, 1756 0, 260, 984, 1868 0, 652, 928, 1492 0, 544, 1240, 1924 312, 0, 532, 1572 0, 184, 936, 1528 0, 180, 1072, 1640 0, 516, 968, 1852 0, 680, 820, 1496 0, 156, 1048, 1676 0, 576, 1272, 1920 0, 232, 848, 1468 0, 0, 720, 1608 0, 232, 1096, 1392 0, 544, 1216, 1960 0, 340, 868, 1552 0, 56, 692, 1552 0, 132, 1024, 1560 0, 232, 904, 1524 0, 544, 1296, 1952 0, 240, 904, 1620 0, 336, 980, 1512 0, 260, 740, 1416 0, 396, 952, 1664 0, 156, 904, 1620 0, 80, 776, 1696 0, 368, 948, 1428 0, 312, 864, 1492 0, 104, 692, 1460 0, 240, 740, 1512 0, 236, 768, 1504 0, 644, 1232, 1948 180, 0, 624, 1248 0, 312, 924, 1604 180, 0, 568, 1284 0, 232, 848, 1380 0, 292, 624, 1340 0, 652, 1040, 1992 164, 0, 524, 1416 0, 104, 632, 1668 0, 184, 744, 1868 444, 0, 668, 1468 288, 0, 708, 1240 344, 0, 480, 1256 0, 336, 756, 1940 0, 312, 760, 1652 0, 492, 1272, 1928 0, 260, 704, 1564 360, 0, 756, 1256 236, 0, 540, 1076 0, 104, 884, 1276 260, 0, 564, 1280 340, 0, 732, 1324 344, 0, 648, 1148 0, 732, 1340, 1848 188, 0, 328, 976 0, 628, 1128, 1724 0, 0, 644, 1292 216, 0, 632, 1492 336, 0, 756, 1436 0, 676, 1208, 1948 316, 0, 540, 1284 516, 0, 856, 1328 388, 0, 756, 1168 264, 0, 596, 1156 156, 0, 708, 980 416, 0, 584, 1296 388, 0, 868, 1192 268, 0, 684, 1160 0, 756, 1280, 1788 180, 0, 520, 1344 140, 0, 552, 968 0, 628, 1124, 1868 388, 0, 776, 1372 232, 0, 600, 1160 156, 0, 572, 924 260, 0, 540, 1076 156, 0, 496, 1084 0, 748, 1504, 1708 420, 0, 700, 1352 256, 0, 620, 1036 308, 0, 648, 1120 0, 652, 1372, 1732 260, 0, 508, 1428 208, 0, 768, 1212 0, 620, 1292, 1948 0, 680, 1316, 1648 236, 0, 768, 1092 344, 0, 648, 1092 0, 728, 1228, 1824 0, 680, 1316, 1824 156, 0, 412, 1120 264, 0, 680, 1036 232, 0, 540, 988 208, 0, 572, 1076 372, 0, 896, 1192 260, 0, 756, 1116 440, 0, 888, 1360 260, 0, 624, 1068 0, 724, 1368, 1848 184, 0, 652, 1044 284, 0, 568, 1220 288, 0, 928, 1280
Эксперимент №5 - источник строго справа
receiver_4,receiver_3,receiver_2,receiver_1 0, 704, 1508, 1956 0, 524, 1308, 1924 0, 652, 1156, 1776 0, 728, 1284, 1908 0, 724, 1532, 1952 0, 704, 1488, 1840 0, 672, 1480, 1960 0, 704, 1340, 1876 0, 680, 1516, 1956 0, 544, 1492, 1904 0, 724, 1448, 1920 0, 728, 1392, 1988 0, 680, 1372, 1908 0, 548, 1384, 1884 0, 620, 1456, 1936 0, 676, 1432, 1992 0, 656, 1548, 1932 0, 676, 1564, 1924 0, 524, 1276, 1924 0, 652, 1544, 1988 0, 704, 1484, 1984 0, 680, 1428, 1936 0, 780, 1368, 1956 0, 620, 1568, 1984 0, 704, 1484, 1896 0, 676, 1480, 1840 0, 652, 1320, 1940 0, 596, 1320, 1944 0, 704, 1232, 1944 0, 628, 1464, 1996 0, 696, 1480, 1952 0, 680, 1516, 1988 0, 776, 1528, 1920 0, 572, 1292, 1948 0, 656, 1520, 1960 0, 696, 1560, 1924 0, 576, 1128, 1992 0, 884, 1556, 1908 0, 748, 1364, 1836 0, 704, 1512, 1984 0, 652, 1488, 1960 0, 672, 1204, 1948 0, 732, 1480, 1956 0, 524, 1076, 1852 0, 836, 1584, 1912 0, 728, 1456, 1984 0, 652, 1456, 1876 0, 808, 1476, 1920 0, 696, 1396, 1992 0, 756, 1392, 1900 0, 756, 1508, 1980 0, 696, 1424, 1952 0, 576, 1348, 1740 0, 732, 1092, 1980 0, 828, 1528, 1912 0, 780, 1364, 1988 0, 752, 1420, 1928 0, 600, 1268, 1800 0, 756, 1480, 1952 0, 776, 1392, 1840 0, 784, 1504, 1980 0, 784, 1532, 1952 0, 780, 1280, 1992 0, 680, 1400, 1964 0, 780, 1480, 1980 0, 628, 1292, 1740 0, 756, 1424, 1988 0, 628, 1184, 1956 0, 672, 1316, 1968 0, 628, 1320, 1944 0, 704, 1264, 2000 0, 756, 1456, 1956 0, 680, 1544, 1988
После сбора данных, самое время перейти к самому интересному - их обработке. Для этого добро пожаловать в следующую главу.
7. Обработка результатов эксперимента
В этой главе нам придется немного коснуться математики и триногометрии.
Сначала рассмотрим простой вариант, когда плоский фронт волны падает на два элемента антенной решетки:

На картинке выше фронт волны падает на два приемника под углом α, при этом между приходом на первый приемник (слева) и приходом на второй (справа) есть задержка Δt Если скорость распространения V а расстояние между приемниками ΔX, то угол α можно определить из соотношения:
Косинус - это отношение прилежащего катета к гипотенузе, гипотенуза в нашем случае это ΔX, а прилежащий катет Δt∗V.
Правило КО МНЕ
Очень легко запомнить что такое косинус КОсинус - отношение (КО МНЕ) прилежашего катета к гипотенузе. Значит синус - отношение противолежащего катета к гипотенузе, КОтангенс - отношение (КО МНЕ) прилежащего катета к противолежащему, А тангенс - отношение противолежащего к прилежащему.
Даже на этом соотношении можно уже делать угломерную систему из двух приемников. Но, как мы говорили выше, так делать не стоит - ошибка измерения времени даже на одном из приемников приведет к совершенно неверному результату. В таких случаях логично увеличить число приемников и пусть голос каждого будет учтен.
Сначала сделаем допущение, что на нашу антенну падает плоский фронт волны. То есть, если смотреть сверху, то он является линией. Такое вполне можно допустить, если расстояние от источника сильно больше, чем размер нашей антенны. Конечно, в бассейне это не соблюдается, но нам все-таки надо от чего-то отталкивать, верно?

На схеме выше показана система координат с центром в первом приемнике, ось X направлена как обычно вправо, а вот по оси Y у нас откладывается время, умноженное на скорость распространения волны.
Теперь, допустим мы сделали измерение и получили какие-то времена прихода сигнала на каждый из приемников. Соответствующее каждому приемнику время умноженное на скорость звука в воде отмечаем при помощи зеленых кружков.
Теперь очень важный момент: если мы считаем фронт волны плоским, то зеленые кружочки должны выстроиться в линию: фронт волны приходит на каждый из приемников через время, пропорционально расстоянию этого приемника от начала координат и углу, под которым этот фронт падает на линейную решетку.
Но, всегда ведь есть какое-то "но", правда?
Кружочки не выстраиваются в ровную линию - всегда есть какая-то ошибка измерения. Но нам нужно учесть вклад каждого времени распространения, по сути нужно так расположить линию, чтобы она проходила максимально близко к каждому зеленому кружочку. В математическом смысле максимально близко.
В 1929 году Эдвин Хаббл именно таким образом и сформулировал свой знаменитый закон, проведя линию по совокупности измерений.

Хаббл тоже получал зашумленные измерения и наносил на график точки, откладывая по оси Х расстояние до Цефеид в ближайших галактиках, а по оси Y их скорость. Надо конечно отдать должное Хабблу - нужна существенная доля смелости, чтобы провести через такое облако точек линию и настаивать именно на линейном характере зависимости скорости удаления от расстояния.
Нам гораздо проще - мы уже знаем, что должна быть линия, нужно только ее правильно провести: аппроксимировать набор точек линией.
Хорошая новость состоит в том, что тут нет ничего сложного: провести линию, значит получить ее уравнение. Уравнение линии простое и имеет общий вид y=kx+b. То есть требуется определить коэффициенты k и b и, забегая вперед скажем, что коэффициент b нам в общем-то и не нужен.
А коэффициент k определяется по такой формуле:
xi - Это координаты наших приемников: 0 - для первого, 1 - для второго, коль скоро мы условились, что расстояния между ними равны 1 метру. yi - это измеренные времена прихода, умноженные на скорость звука в воде.
Теперь еще один очень важный момент: коэффициент k - это тангенс угла наклона линии, относительно оси X. Взгляните еще раз на схему выше:
А мы помним, что:
И получается интересная вещь, что:
Отсюда мы легко можем посчитать угол α - угол прихода сигнала. Еще раз, вот вся последовательность измерений и вычислений:
Определяем времена прихода на все элементы антенной решетки
При помощи линейной аппроксимации находим коэффициент k уравнения прямой - это по определению и есть тангенс угла наклона этой прямой
Угол прихода сигнала определяем как арккосинус от k
Все эти данные затруднительно представить при помощи графика в Excel или в чем-то похожем, поэтому придется прибегнуть к Matlab, точнее к его свободному аналогу - GNU Octave.
Данные для обработки удобно сохранить в отдельные файлы "как есть" - то есть в формате CSV. Мы сохраним их под именами data1.csv, data2.csv, data3.csv, data4.csv и data5.csv.
Для их обработки мы подготовили скрипт
clc; clear all; close all; % Параметры обработки Num = 5; % Номер набора данных (1-5) % Загрузка данных data_1 = csvread('data1.csv'); data_2 = csvread('data2.csv'); data_3 = csvread('data3.csv'); data_4 = csvread('data4.csv'); data_5 = csvread('data5.csv'); actual_angles = [180, 135, 90, 45, 0]; % Фактические углы для каждого набора % Выбор данных для обработки if (Num == 1) src_data = data_1; elseif (Num == 2) src_data = data_2; elseif (Num == 3) src_data = data_3; elseif (Num == 4) src_data = data_4; elseif (Num == 5) src_data = data_5; endif % Физические параметры v = 1503.6; % Скорость звука в воде, м/с t_scale = 1 / 1000000; % Масштаб времени (мкс в секунды) % Координаты приемников антенной решетки (ULA) ula4_xs = [3 2 1 0]; ula4_ys = [0 0 0 0]; % Создание одного окна с двумя subplot figure('Position', [100, 100, 1200, 600]); % Увеличенный размер окна %% Первый график: измерения и аппроксимация subplot(1, 2, 1); % 1 строка, 2 столбца, позиция 1 axis equal hold on grid on % Предварительные вычисления для линейной регрессии x_sum = sum(ula4_xs); x2_sum = sum(ula4_xs .^ 2); x_sum2 = x_sum ^ 2; rec_num = length(ula4_xs); % Инициализация массивов k = zeros(length(src_data), 1); % Коэффициенты наклона b = zeros(length(src_data), 1); % Свободные члены alpha = zeros(length(src_data), 1); % Углы прихода % Обработка каждого измерения for n = 1:length(src_data) % Преобразование временных задержек в расстояния ys = src_data(n, :) .* t_scale .* v; % Проверка данных на корректность if ~all(isfinite(ys)) warning('Обнаружены некорректные данные в строке %d. Пропускаю.', n); continue; end % Построение графиков измерений if n == 1 plot(ula4_xs, ys, 'b.', 'DisplayName', 'Измерения V·t', 'MarkerSize', 4); else plot(ula4_xs, ys, 'b.', 'HandleVisibility', 'off', 'MarkerSize', 4); end % Вычисление коэффициентов прямой (метод наименьших квадратов) y_sum = sum(ys); xy_sum = sum(ula4_xs .* ys); denominator = (rec_num * x2_sum - x_sum2); if denominator == 0 warning('Вырожденная система в строке %d. Пропускаю.', n); continue; end k(n) = (rec_num * xy_sum - x_sum * y_sum) / denominator; b(n) = (y_sum - k(n) * x_sum) / rec_num; % Построение линии аппроксимации x_range = [0, 3]; y_range = k(n) * x_range + b(n); if n == 1 line(x_range, y_range, 'Color', 'red', 'LineWidth', 0.5, 'DisplayName', 'Линейная аппроксимация'); else line(x_range, y_range, 'Color', 'red', 'LineWidth', 0.5, 'HandleVisibility', 'off'); end % Вычисление угла прихода через арккосинус k_bound = max(min(k(n), 1), -1); % Ограничение для области определения acos alpha(n) = acos(-k_bound); end % Отображение позиций приемников plot(ula4_xs, ula4_ys, 'go', 'MarkerSize', 8, 'MarkerFaceColor', 'g', 'DisplayName', 'Элементы антенны'); % Настройка первого графика tstr = sprintf("Линейная аппроксимация tgφ=cosα\nФактический угол: %d°, выборка: %d",... actual_angles(Num), length(src_data)); title(tstr, "fontsize", 14); xlabel('X, m', "fontsize", 14); ylabel('V·t, m', "fontsize", 14); lh = legend('show'); set(lh, "fontsize", 12); %% Второй график: гистограмма углов subplot(1, 2, 2); % 1 строка, 2 столбца, позиция 2 alpha_deg = rad2deg(alpha); % Преобразование радиан в градусы % Статистика углов mean_alpha = mean(alpha_deg); std_alpha = std(alpha_deg); max_alpha = max(alpha_deg); min_alpha = min(alpha_deg); % Построение гистограммы nbins = 20; [counts, bins] = hist(alpha_deg, nbins); hist(alpha_deg, nbins, 'DisplayName', 'α, °'); colormap(summer()); hold on; y_limits = ylim; % Линии статистик на гистограмме plot([mean_alpha, mean_alpha], y_limits, 'r-', 'LineWidth', 2,... 'DisplayName', sprintf('Среднее = %.1f°', mean_alpha)); plot([mean_alpha - std_alpha, mean_alpha - std_alpha], y_limits, 'g--',... 'LineWidth', 1.5, 'DisplayName', sprintf('±1 СКО (%.1f°)', std_alpha)); plot([mean_alpha + std_alpha, mean_alpha + std_alpha], y_limits, 'g--',... 'LineWidth', 1.5, 'HandleVisibility', 'off'); % Настройка второго графика llh = legend('show'); set(llh, "fontsize", 12); tstr = sprintf("Определение угла прихода\nФактический угол: %d°, выборка: %d",... actual_angles(Num), length(src_data)); title(tstr, "fontsize", 14); xlabel('Угол α, °', "fontsize", 14); ylabel('Частота', "fontsize", 14); % Вывод статистики в консоль fprintf('\n=== СТАТИСТИКА УГЛОВ ===\n'); fprintf('Среднее: %.2f°\n', mean_alpha); fprintf('СКО: %.2f°\n', std_alpha); fprintf('Минимум: %.2f°\n', min_alpha); fprintf('Максимум: %.2f°\n', max_alpha); fprintf('Размах: %.2f°\n', max_alpha - min_alpha);
Напомним пару моментов:
углы мы отсчитываем против часовой стрелки от горизонтали.
у нас есть 5 наборов данных, полученных для углов 180, 135, 90, 45 и 0°.
Теперь давайте давайте двайте же уже посмотрим на результаты!
Для каждого взаимного расположения антенной решетки и источника мы получим два графика, на одном будем отображать измеренные времена прихода и линию, аппроксимирующую эти измерения, а на втором гистограмму распределения вычисленного угла прихрда.
Итак, для угла 180° - источник строго слева от антенной решетки:





Из полученной нами статистики можно заключить, что в целом макет полностью рабочий, особенно с учетом того, что малый бассейн - чрезвычайно сложная "акватория" для подобных систем.
Мы видим, что в среднем СКО (среднеквадратичное отклонение) определения угла прихода составляет порядка 6°. Среднее значение по выборке соответствует фактическому углу во всех экспериментах, кроме эксперимента №2 (135°). Особенно хороший результат получился при 90° и при 45°.
Как можно улучшить эту систему?
Во-первых, обеспечить фиксированное положение антенных элементов (приемников), нарастив им кабеля и усовершенствовав раму.
А во-вторых, было бы интересно посмотреть на результат в более щадящих условиях, например в небольшом естественном водоеме - очень вероятно, что там результаты окажутся гораздо лучше.
Предлагаем глубокоуважаемому продвинутому читателю самому разобраться вот в каком вопросе: мы сделали допущение, что в наших экспериментах фронт мы считаем плоским. Но в действительности считать его таковым можно только в случае углов 180° и 0°, когда источник находится на оси антенной решетки. А если посмотреть на график с измерениями для угла 90°, то невооруженным глазом видно, что вместо линии лучше бы подошла дуга окружности. Да, это скорее задача с двумя звездочками для начинающих, но успешно решив ее, можно совершенно заслуженно поставить самому себе плюсик в карму.
Ну и давайте подытожим:
мы оценили, насколько флуктуируют определяемые времена прихода сигнала в разных приемниках
на основе этих данных мы сделали угломерную систему на основе 4-х элементной антенной решетки
сделали простейшее оптимизационное решение на основе линейной аппроксимации
провели натурные эксперименты в небольшом бассейне, которые подтвердили работоспособность системы
оценили статистические свойства полученного результата
8. Outro
Поздравим и похвалим сами себя с завершением одного из самых сложных проектов этого цикла - вы великолепны!
Кто просто прочитал - тоже великолепен, мы прекрасно понимаем, что тема очень узкая, нишевая, и если вы с нами на одной волне - мы вам благодарны просто за то, что вы есть =)
Нам очень важна обратная связь - конструктивная критика, вопросы, пожелания - не стесняйтесь высказываться. У нас полно идей для подобных материалов, и если они востребованы, то по мере возникновения свободного времени мы будем их публиковать.
Ваши, до глубины души
@AlekDikarev
@StDmitriev
@Creathor
