Please use this identifier to cite or link to this item: https://er.chdtu.edu.ua/handle/ChSTU/7123
Title: Програмна платформа комплексу оптичної астронавігації
Authors: Голуб, Сергій Васильович
Веретільник, Віталій Олегович
Keywords: АСТРОНОМІЯ;ANDROID;KOTLIN;СЕНСОРИ;АКСЕЛЕРОМЕТР;ГІРОСКОП;МАГНІТОМЕТР;КАМЕРА;RAW-ФОТОЗЙОМКА;ОБРОБКА ДАНИХ;ЗОРЯНИЙ ЧАС;GPS;БАЗА ДАНИХ;SQLITE;КАТАЛОГ ЗІРОК;МОБІЛЬНИЙ ДОДАТОК;ASTRONOMY;ANDROID;KOTLIN;SENSORS;ACCELEROMETER;GYROSCOPE;MAGNETOMETER;CAMERA;RAW PHOTOGRAPHY;DATA PROCESSING;SIDERAL TIME;GPS;DATABASE;SQLITE;STAR CATALOG;MOBILE APPLICATION
Issue Date: 12-Dec-2025
Abstract: АНОТАЦІЯ Прізвище та ініціали студента: Веретільник В.О. Назва КРМ: Програмна платформа комплексу оптичної астронавігації Спеціальність (шифр і назва): 121 «Інженерія програмного забезпечення» Установа, де відбудеться захист: Черкаський державний технологічний університет. Місто, рік: Черкаси, 2025 У роботі представлено розробку мобільного додатку «POA» для платформи Android, створеного для виконання астрономічних, геолокаційних та оптичних вимірювань. Додаток дозволяє використовувати сенсори смартфона - акселерометр, гіроскоп і магнітометр - для визначення орієнтації пристрою, напрямку руху та азимуту. Реалізовано функції розрахунку місцевого зоряного часу, визначення координат за допомогою GPS та відображення напрямку на Полярну зорю. Окрему увагу приділено оновленому модулю камери, який забезпечує серійну фотозйомку у форматі RAW з фіксованим часовим інтервалом. Для кожного зображення автоматично формується набір метаданих, що містить покази сенсорів, часову мітку, географічні координати та обчислене значення місцевого зоряного часу, що забезпечує синхронізацію оптичних, сенсорних та астрономічних даних. Також створено локальний каталог зірок, який зберігається у вбудованій базі даних і доступний для швидкого пошуку. Додаток поєднує роботу сенсорів, астрономічні обчислення, модуль камери та механізми обробки експериментальних даних, а також інтерактивний інтерфейс, створюючи зручний інструмент для навчальних, дослідницьких і пізнавальних цілей у сфері астрономії.
ANNOTATION Student's last name and initials: Veretilnyk V.O. Name of KRM: Software platform of the optical astronavigation system Specialty (code and name): 121 "Software engineering" Institution where the defense will take place: Cherkasy State Technological University. City, year: Cherkasy, 2025 The work presents the development of the mobile application "POA" for the Android platform, created to perform astronomical, geolocation and optical measurements. The application allows you to use smartphone sensors - accelerometer, gyroscope and magnetometer - to determine the orientation of the device, direction of movement and azimuth. The functions of calculating local sidereal time, determining coordinates using GPS and displaying the direction to the Polaris are implemented. Special attention is paid to the updated camera module, which provides serial photography in RAW format with a fixed time interval. For each image, a set of metadata is automatically generated, containing sensor readings, a timestamp, geographic coordinates, and the calculated value of local sidereal time, which ensures synchronization of optical, sensor, and astronomical data. A local star catalog is also created, which is stored in a built-in database and is available for quick search. The application combines sensor operation, astronomical calculations, a camera module, and experimental data processing mechanisms, as well as an interactive interface, creating a convenient tool for educational, research, and cognitive purposes in the field of astronomy.
URI: https://er.chdtu.edu.ua/handle/ChSTU/7123
Appears in Collections:121 Інженерія програмного забезпечення (Інженерія програмного забезпечення)



Items in DSpace are protected by copyright, with all rights reserved, unless otherwise indicated.

Extracted text
МІНІСТЕРСТВО ОСВІТИ І НАУКИ УКРАЇНИ 
ЧЕРКАСЬКИЙ ДЕРЖАВНИЙ ТЕХНОЛОГІЧНИЙ УНІВЕРСИТЕТ 
Факультет інформаційних технологій і систем 
Кафедра програмного забезпечення автоматизованих систем 
 
 
ПОЯСНЮВАЛЬНА ЗАПИСКА 
до кваліфікаційної роботи 
магістра 
 
 
на тему:  «Програмна платформа комплексу оптичної астронавігації»  
 
 
Виконав: студент 2 курсу, групи МПЗ-2404 
спеціальності  
121 «Інженерія програмного забезпечення»  
(шифр і назва напряму підготовки) 
 
 
Студент Веретільник В.О. 
 (прізвище та ініціали) 
Керівник Голуб С.В. 
 (прізвище та ініціали) 
Рецензент Завалко Є.В. 
 (прізвище та ініціали) 
 
 
 
Черкаси 2025 
 
 
 
Черкаський державний технологічний університет                                                                                    
повне найменування вищого навчального закладу 
Факультет інформаційних технологій і систsем 
Кафедра  програмного забезпечення автоматизованих систем 
Освітній рівень магістр 
Спеціальність 121 «Інженерія програмного забезпечення» 
Освітня програма Інженерія програмного забезпечення 
 
ЗАТВЕРДЖУЮ 
Зав. кафедри ПЗАС, професор 
                                           С. Голуб  
«___» _______________ 2025 року 
 
З А В Д А Н Н Я 
НА ДИПЛОМНУ РОБОТУ СТУДЕНТУ 
Веретільник Віталій Олегович 
(прізвище, ім’я, по батькові) 
1. Тему проекту (роботи) Програмна платформа комплексу оптичної астронавігації 
Керівник проекту (роботи) Голуб Сергій Васильович д.т.н., професор  
                      (прізвище, ім’я , по батькові, науковий ступінь, вчене звання) 
Затверджені наказом Черкаського державного технологічного університету від «7 жовтня»  
2025 року №307/03-03 
2.Строк подання студентом проекту (роботи) 09.12.2025  
3. Вхідні дані до проекту (роботи)   
4. Зміст розрахунково-пояснювальної записки (перелік питань, які потрібно розробити)  
Розділ 1. Існуючі методи та засоби розв’язання поставлених завдань 
Розділ 2. Теоретичні та експерементальні дослідження 
Розділ 3. Впровадження результатів дослідження у практику проектування програмного 
забезпечення інформаційних систем 
Розділ 4. Розробка та тестування програмного забезпечення 
Висновки; 
Список використаних джерел; 
Додатки А, Додатки Б, Додатки В. 
5. Перелік графічного матеріалу (з точним зазначенням обов’язкових робіт проекту; 
слайд 1, слайд 2, слайд 3, слайд 4, слайд 5, слайд 6, слайд 7, слайд 8, слайд 9, слайд 10, слайд 11, 
слайд 12, слайд 13, слайд 14, слайд 15, слайд 16, слайд 17, слайд 18, слайд 19, слайд 20, слайд 
21, слайд 22, слайд 23, слайд 24, слайд 25, слайд 26, слайд 27, слайд 28, слайд 29, слайд 30, 
слайд 31, слайд 32, слайд 33, слайд 34, слайд 35, слайд 36.  
  
6. Консультанти розділів роботи 
Прізвище, ініціали та посади Підпис, дата 
Розділ 
консультанта Завдання видав Завдання прийняв 
1    
2    
3    
 
7. Дата видачі завдання    вересень   2025 р. 
  
 
 
КАЛЕНДАРНИЙ ПЛАН 
Строк виконання 
№ етапів 
Назва етапів випускної роботи Примітки 
п/п кваліфікаційної 
роботи 
1 Постановка задачі 05.08.2025 виконано 
2 Підготовка завдання 10.08.2025 виконано 
3 Погодження завдання 20.08.2025 виконано 
4 Затвердження завдання 25.08.2025 виконано 
 Основна стадія   
1 Підбір матеріалів 01.09.2025 виконано 
2 Аналіз шляхів вирішення 03.09.2025 виконано 
поставленої задачі 
3 Розрахунок основних параметрів 10.10.2025 виконано 
роботи 
4 Вибір кінцевого варіанту 15.11.2025 виконано 
проектного рішення 
5 Оформлення первісної редакції 20.11.2025 виконано 
роботи 
 Заключна стадія   
1 Узгодження прийнятих проектних 02.12.2025 виконано 
рішень з керівником 
2 Оформлення пояснювальної 04.12.2025 виконано 
записки роботи в кінцевій редакції 
3 Попередній захист роботи 06.12.2025 виконано 
4 Затвердження роботи 06.12.2025 виконано 
5 Рецензування роботи 07.12.2025 виконано 
6 Захист роботи 09.12.2025 виконано 
 
Студент                                 _____________________ Веретільник В.О. 
  (підпис)   (прізвище та ініціали) 
 
Керівник роботи _____________________   Голуб С.В. 
  (підпис)   (прізвище та ініціали) 
  
 
 
АНОТАЦІЯ 
Прізвище та ініціали студента: Веретільник В.О. 
Назва КРМ: Програмна платформа комплексу оптичної астронавігації 
Спеціальність (шифр і назва): 121 «Інженерія програмного забезпечення»  
Установа, де відбудеться захист:  Черкаський державний технологічний 
університет. 
Місто, рік: Черкаси, 2025 
У роботі представлено розробку мобільного додатку «POA» для платформи 
Android, створеного для виконання астрономічних, геолокаційних та оптичних 
вимірювань. Додаток дозволяє використовувати сенсори смартфона - 
акселерометр, гіроскоп і магнітометр - для визначення орієнтації пристрою, 
напрямку руху та азимуту. Реалізовано функції розрахунку місцевого зоряного 
часу, визначення координат за допомогою GPS та відображення напрямку на 
Полярну зорю. Окрему увагу приділено оновленому модулю камери, який 
забезпечує серійну фотозйомку у форматі RAW з фіксованим часовим інтервалом. 
Для кожного зображення автоматично формується набір метаданих, що містить 
покази сенсорів, часову мітку, географічні координати та обчислене значення 
місцевого зоряного часу, що забезпечує синхронізацію оптичних, сенсорних та 
астрономічних даних. 
Також створено локальний каталог зірок, який зберігається у вбудованій 
базі даних і доступний для швидкого пошуку. Додаток поєднує роботу сенсорів, 
астрономічні обчислення, модуль камери та механізми обробки 
експериментальних даних, а також інтерактивний інтерфейс, створюючи зручний 
інструмент для навчальних, дослідницьких і пізнавальних цілей у сфері 
астрономії. 
Ключові слова: АСТРОНОМІЯ, ANDROID, KOTLIN, СЕНСОРИ, 
АКСЕЛЕРОМЕТР, ГІРОСКОП, МАГНІТОМЕТР, КАМЕРА, RAW-
ФОТОЗЙОМКА, ОБРОБКА ДАНИХ, ЗОРЯНИЙ ЧАС, GPS, БАЗА ДАНИХ, 
SQLITE, КАТАЛОГ ЗІРОК, МОБІЛЬНИЙ ДОДАТОК.  
 
 
ANNOTATION 
Student's last name and initials: Veretilnyk V.O. 
Name of KRM: Software platform of the optical astronavigation system 
Specialty (code and name): 121 "Software engineering" 
Institution where the defense will take place: Cherkasy State Technological 
University. 
City, year: Cherkasy, 2025 
The work presents the development of the mobile application "POA" for the 
Android platform, created to perform astronomical, geolocation and optical 
measurements. The application allows you to use smartphone sensors - accelerometer, 
gyroscope and magnetometer - to determine the orientation of the device, direction of 
movement and azimuth. The functions of calculating local sidereal time, determining 
coordinates using GPS and displaying the direction to the Polaris are implemented. 
Special attention is paid to the updated camera module, which provides serial 
photography in RAW format with a fixed time interval. For each image, a set of 
metadata is automatically generated, containing sensor readings, a timestamp, 
geographic coordinates, and the calculated value of local sidereal time, which ensures 
synchronization of optical, sensor, and astronomical data. 
A local star catalog is also created, which is stored in a built-in database and is 
available for quick search. The application combines sensor operation, astronomical 
calculations, a camera module, and experimental data processing mechanisms, as well 
as an interactive interface, creating a convenient tool for educational, research, and 
cognitive purposes in the field of astronomy. 
Keywords: ASTRONOMY, ANDROID, KOTLIN, SENSORS, 
ACCELEROMETER, GYROSCOPE, MAGNETOMETER, CAMERA, RAW 
PHOTOGRAPHY, DATA PROCESSING, SIDERAL TIME, GPS, DATABASE, 
SQLITE, STAR CATALOG, MOBILE APPLICATION. 
 
 
ЗМІСТ 
ПЕРЕЛІК УМОВНИХ ПОЗНАЧЕНЬ ..................................................................... 4 
ВСТУП ...................................................................................................................... 5 
РОЗДІЛ 1 ІСНУЮЧУ МЕТОДИ ТА ЗАСОБИ РОЗВ’ЯЗАННЯ ПОСТАВЛЕНИХ 
ЗАДАЧ ............................................................................................................................ 12 
1.1 Аналіз існуючих рішень ................................................................................... 12 
1.2 Існуючі методи та засоби ................................................................................. 16 
1.3 Актуальні проблеми .......................................................................................... 18 
1.4 Методи вирішення проблем ............................................................................. 18 
1.5 Систематизація отриманих даних ................................................................... 19 
1.6 Постановка задачі досліджень ......................................................................... 20 
ВИСНОВОК ДО РОЗДІЛУ 1 ....................................................................................... 22 
РОЗДІЛ 2 ТЕОРЕТИЧНІ ТА ЕКСПЕРИМЕНТАЛЬНІ ДОСЛІДЖЕННЯ .............. 23 
2.1 Теоретичні дослідження ................................................................................... 23 
2.1.1 Формалізація задачі дослідження ........................................................... 23 
2.1.2 Математичні моделі сенсорних вимірювань та орієнтації .................. 23 
2.1.3. Математичні моделі астрономічного часу та навігації ....................... 25 
2.1.4 Модель програмного комплексу ............................................................ 25 
2.2. Експериментальні дослідження ...................................................................... 26 
2.2.1 Експеримент №1. Валідація точності орієнтації та пошуку 
Полярної зорі ..................................................................................................... 26 
2.2.2 Експеримент №2. Оцінка продуктивності графічної підсистеми 3D-
візуалізації .......................................................................................................... 26 
2.2.3 Експеримент №3. Формування мультимодального дата сету ............. 26 
2.2.4 Експеримент №4. Аналіз латентності бази даних ................................ 28 
ВИСНОВОК ДО РОЗДІЛУ 2 ....................................................................................... 29 
РОЗДІЛ 3 ВПРОВАДЖЕННЯ РЕЗУЛЬТАТІВ ДОСЛІДЖЕНЬ У ПРАКТИКУ 
ПРОЕКТУВАННЯ ПРОГРАМНОГО ЗАБЕЗПЕЧЕННЯ ІНФОРМАЦІЙНИХ 
СИСТЕМ ........................................................................................................................ 30 
3.1 Модель предметної області .............................................................................. 30 
3.1.1 Предметна область моделювання. Модель предметної області. 
Словник предметної області .................................................................. 30 
3.1.2 Елементи моделювання ........................................................................... 32 
3.1.3 Робоча область моделювання: Програмна платформа комплексу 
оптичної астронавігації .......................................................................... 35 
3.2 Формування та аналіз вимог ............................................................................ 36 
3.2.1 Формування вимог до програмного забезпечення. Первинні і 
детальні вимоги. Вимоги замовника. Функціональні та 
нефункціональні вимоги ........................................................................ 37 
3.2.2 Формування вимог за допомогою діаграми прецедентів .................... 40 
3.2.3 Проектування логічної структури програмного комплексу ................ 41 
3.2.3.1 Діаграми класів ........................................................................... 42 
3.2.3.2 Діаграми пакетів ......................................................................... 44 
3.2.4 Архітектурне проектування .................................................................... 46 
3.2.4.1 Діаграма компонентів ................................................................. 47 
3.2.4.2 Діаграма розгортання ................................................................. 48 
3.2.5 Моделювання поведінки системи .......................................................... 49 
3.2.5.1 Діаграма діяльності ..................................................................... 49 
3.2.5.2 Діаграма послідовності .............................................................. 51 
3.2.5.3 Діаграма комунікації .................................................................. 52 
3.2.5.4 Діаграма скінченного автомату ................................................. 53 
ВИСНОВОК ДО РОЗДІЛУ 3 ....................................................................................... 54 
РОЗДІЛ 4 РОЗРОБКА ТА ТЕСТУВАННЯ ПРОГРАМНОГО ЗАБЕЗПЕЧЕННЯ .. 55 
4.1 Розробка програмного комплексу ................................................................... 55 
4.1.1 Обґрунтування вибору засобів реалізації .............................................. 55 
4.1.2 Опис структурної (функціональної) схеми ........................................... 57 
4.1.3 Опис логічної схеми системи .................................................................. 59 
4.1.4 Розробка бази даних................................................................................. 61 
4.1.5 Розробка інтерфейсу користувача .......................................................... 62 
4.1.6 Опис розробки програмних компонентів .............................................. 69 
4.2 Тестування системи .......................................................................................... 71 
4.2.1 Модульне тестування .............................................................................. 72 
4.2.2 Інтеграційне тестування .......................................................................... 73 
4.2.3 Системне тестування ............................................................................... 74 
4.2.4 Приймальне тестування ........................................................................... 74 
4.3 Приклади впровадженого програмного комплексу ....................................... 78 
ВИСНОВОК ДО РОЗДІЛУ 4 ....................................................................................... 80 
ВИСНОВКИ ................................................................................................................... 81 
СПИСОК ВИКОРИСТАНИХ ДЖЕРЕЛ ..................................................................... 83 
ДОДАТОК А .................................................................................................................. 85 
ДОДАТОК Б................................................................................................................... 87 
ДОДАТОК В .................................................................................................................. 113 
ЧДТУ 252477.002 ПЗ 
ПЕРЕЛІК УМОВНИХ ПОЗНАЧЕНЬ 
ПЗ  - програмне забезпечення 
БД  -  база даних 
API - програмний інтерфейс взаємодії 
GPS - глобальна система позиціонування 
LST - місцевий зоряний час (Local Sidereal Time) 
SQLite - легка вбудована система керування базами даних 
UI - користувацький інтерфейс (User Interface) 
UX - досвід користувача (User Experience) 
SDK - набір засобів розробника (Software Development Kit) 
IDE - інтегроване середовище розробки 
Kotlin - мова програмування, використана для розробки 
Android - мобільна операційна система 
Sensor API - інтерфейс взаємодії з сенсорами мобільного пристрою 
Accelerometer - акселерометр 
Gyroscope - гіроскоп 
Magnetometer - магнітометр 
DB Helper - допоміжний клас для роботи з базою даних 
 
4 
ЧДТУ 252477.002 ПЗ 
ВСТУП 
Актуальність теми. Актуальність розробки програмного забезпечення 
платформи комплексу оптичної астронавігації зумовлена необхідністю створення 
сучасних, автономних та точних систем орієнтації у просторі, що не залежать від 
зовнішніх радіосигналів чи супутникових мереж. Використання сенсорів 
мобільного пристрою - акселерометра, гіроскопа та магнітометра - дозволяє 
визначати орієнтацію, рух та азимут об’єктів у реальному часі, що є критично 
важливим для астрономічних та геопросторових обчислень. 
Інтеграція функцій обчислення місцевого зоряного часу (LST), визначення 
GPS-координат та візуалізації напрямку на Полярну Зорю забезпечує точне 
визначення положення на небесній сфері. Локальна база даних зірок та її 
ефективна обробка роблять платформу зручною для навчальних, дослідницьких 
та практичних застосувань. 
Додаткові можливості, такі як 3D-моделювання Землі, відображення нахилу 
осі та інтерактивні елементи інтерфейсу, роблять систему наочною та зручною 
для користувачів. Інтеграція Python-скриптів відкриває перспективи для 
розширення аналітичних функцій платформи. 
Таким чином, створення цієї програмної платформи є важливим і 
своєчасним кроком у розвитку оптичної астронавігації, надаючи сучасний 
інструмент для точних вимірювань, орієнтації та навчання в астрономії та 
геопозиціюванні. 
Мета та завдання розробки. Метою розробки є дослідження процесу та 
створення програмного забезпечення платформи комплексу оптичної 
астронавігації для мобільних пристроїв на базі Android, яка дозволяє виконувати 
точні фізичні вимірювання, астрономічні обчислення та 3D-візуалізацію для 
навчальних, дослідницьких та практичних цілей. 
Сформульована мета досягається шляхом теоретичного обґрунтування та 
практичної реалізації методів синтезу апаратних засобів мобільних платформ із 
математичним апаратом сферичної астрономії. Це передбачає розв’язання задач 
класифікації небесних тіл, ідентифікації функціональних залежностей результатів 
5 
ЧДТУ 252477.002 ПЗ 
визначення просторового положення від ознак, отриманих з інерціальних 
сенсорів, та валідації отриманих моделей. 
Завданнями розробки є: 
1 Розробка програмних засобів обробки даних акселерометра, гіроскопа та 
магнітометра для розв’язання зворотної задачі просторової орієнтації 
смартфона та визначення кутів нахилу й азимуту. 
2 Реалізація алгоритмів астрономічних обчислень, зокрема розрахунку 
Юліанської дати (JD), Гринвічського та місцевого зоряного часу (LST) 
на основі системного часу та географічних координат, отриманих через 
GPS. 
3 Забезпечення визначення напрямку на Полярну зорю шляхом поєднання 
результатів сенсорних вимірювань та астрономічних розрахунків у 
горизонтальній системі координат. 
4 Створення та оптимізація локального каталогу зірок у базі даних SQLite 
з підтримкою ефективного пошуку та відображення основних 
астрономічних характеристик небесних об’єктів. 
5 Розробка геоцентричної 3D-візуалізації Землі з використанням OpenGL 
ES, що підтримує інтерактивне керування, масштабування, обертання та 
відображення маркера місцезнаходження користувача. 
6 Реалізація динамічного візуального відображення нахилу осі обертання 
Землі з використанням анімаційних графічних елементів інтерфейсу. 
7 Уніфікація інтерфейсу користувача та навігації додатку шляхом 
використання базового класу BaseActivity з підтримкою багатомовності 
та єдиного стилю взаємодії. 
8 Інтеграція можливостей виконання Python-скриптів у середовищі 
Android для розширення обчислювального апарату та аналітичної 
обробки астрономічних і сенсорних даних. 
9 Реалізація модуля інтелектуальної фотозйомки на основі Camera2 API, 
що забезпечує серійне захоплення RAW-зображень (DNG) з фіксованим 
часовим інтервалом та формування супровідних JSON-метаданих, які 
6 
ЧДТУ 252477.002 ПЗ 
містять покази сенсорів, часові мітки, географічні координати та 
обчислені астрономічні параметри. 
Для уточнення алгоритмічних підходів, структурування програмного коду 
та формування текстових описів використовувалися сучасні інтелектуальні 
інформаційні сервіси [2]. 
Об'єкт розробки Об'єктом розробки є процес створення програмного 
забезпечення комплексу оптичної астронавігації, яке забезпечує користувачів 
можливістю виконувати фізичні вимірювання, астрономічні обчислення та 3D-
візуалізацію для навчальних, дослідницьких та практичних цілей. 
Предмет розробки. Предметом розробки є процес проектування 
програмного забезпечення мобільного додатку комплексу оптичної астронавігації, 
що включає роботу з сенсорами, обчислення астрономічних параметрів та 3D-
візуалізацію для орієнтації та досліджень. 
Методи проектування та конструювання 
Для реалізації проекту використовувалися такі методи: 
1 Проєктування користувацького інтерфейсу 
Застосовано методи проєктування людино-машинної взаємодії для 
створення інтуїтивно зрозумілої структури мобільного додатку, що 
забезпечує зручну навігацію між сенсорними, астрономічними та 
візуалізаційними модулями, з уніфікацією інтерфейсу через базовий 
клас BaseActivity. 
2 Об’єктно-орієнтована розробка з використанням Kotlin та Android 
SDK 
Реалізація програмної логіки виконувалася мовою Kotlin з 
використанням компонентів Android SDK, що забезпечує модульність, 
масштабованість та підтримку асинхронної обробки даних сенсорів, 
бази даних і графічного рендерингу. 
3 Алгоритмічна обробка сенсорних даних та математичне 
моделювання орієнтації 
Використано методи обробки інерціальних вимірювань акселерометра, 
7 
ЧДТУ 252477.002 ПЗ 
 гіроскопа та магнітометра для розв’язання задачі визначення 
просторової орієнтації пристрою, кутів нахилу та азимуту з 
використанням матриць обертання і тригонометричних перетворень. 
4 Реалізація астрономічних обчислень 
Застосовано алгоритмічні методи сферичної астрономії для обчислення 
Юліанської дати (JD), Гринвічського та місцевого зоряного часу (LST), а 
також для визначення напрямку на Полярну зорю на основі 
географічних координат та часу спостереження. 
5 Проєктування та оптимізація локальної бази даних 
Для зберігання каталогу зірок використано реляційну базу даних SQLite 
з методами пакетної обробки та оптимізованого пошуку, що забезпечує 
швидкий доступ до астрономічної інформації в умовах обмежених 
ресурсів мобільного пристрою. 
6 3D-візуалізація та графічне моделювання 
Застосовано OpenGL ES для побудови геоцентричної тривимірної 
моделі Землі з підтримкою жестів масштабування та обертання, 
відображення маркера місцезнаходження користувача та візуалізації 
нахилу осі обертання планети. 
7 Методи експериментальної реєстрації оптичних даних 
Реалізовано метод серійної RAW-фотозйомки з використанням Camera2 
API, що забезпечує формування експериментальних наборів даних із 
жорсткою часовою та просторовою прив’язкою оптичних зображень до 
сенсорних показів, GPS-координат та астрономічного часу. 
8 Інтеграція Python-компонентів 
Передбачено використання Python-скриптів для розширення 
обчислювальних можливостей системи, зокрема для аналітичної 
обробки експериментальних даних. 
Наукова новизна отриманих результатів 
Наукова новизна роботи: 
Удосконалено метод обробки результатів астронавігаційних спостережень  
8 
ЧДТУ 252477.002 ПЗ 
шляхом переходу від ізольованого аналізу окремих сенсорних вимірювань до 
мультимодального синтезу гетерогенних даних, що формуються мобільним 
пристроєм у процесі спостереження. У межах розробленої програмної платформи 
кожне експериментальне спостереження подається у вигляді багатовимірного 
вектора ознак, який об’єднує показники інерціальних сенсорів, географічні 
координати, часові параметри та результати оптичної реєстрації. 
Реалізовано для мобільних астронавігаційних систем інтегровану 
архітектуру, що поєднує інерційну навігацію смартфона з алгоритмами сферичної 
астрономії в єдиному обчислювальному просторі. Синтез даних акселерометра, 
гіроскопа та магнітометра з автономними обчисленнями Юліанської дати та 
місцевого зоряного часу на основі GPS-координат забезпечує детерміноване 
визначення орієнтації пристрою, азимутального напрямку на Полярну зорю та 
просторову прив’язку навігаційних параметрів до небесної сфери без залучення 
зовнішніх сервісів. 
Подальший розвиток отримала задача локального зберігання та обробки 
астрономічних даних шляхом оптимізації структури бази даних SQLite та 
впровадження пакетної обробки великих масивів зоряного каталогу, що дозволяє 
виконувати швидкий пошук і фільтрацію небесних об’єктів у режимі реального 
часу на обмежених апаратних ресурсах мобільного пристрою. 
Науково значущим результатом є розробка геоцентричної 3D-візуалізації 
Землі з використанням OpenGL ES, у якій реалізовано математичну модель 
просторових перетворень географічних координат у декартову систему з 
подальшим графічним рендерингом. Це забезпечує візуальну валідацію 
результатів астрономічних та навігаційних обчислень і підвищує 
інтерпретованість отриманих даних. 
Окрему наукову новизну становить метод формування експериментальних 
наборів даних шляхом серійної RAW-фотозйомки з одночасною реєстрацією 
сенсорних показів, GPS-координат та астрономічного часу. Запропонований 
підхід забезпечує жорстку логічну прив’язку оптичного спостереження до 
фізичного стану пристрою в момент зйомки, що створює основу для подальшого 
9 
ЧДТУ 252477.002 ПЗ 
застосування методів статистичного аналізу, калібрування навігаційних похибок 
та використання отриманих датасетів у задачах автоматичного розпізнавання 
зоряного неба. 
У цілому отримані результати формують нову методологію побудови 
інтегрованих мобільних систем оптичної астронавігації, що поєднують сенсорні 
вимірювання, астрономічні обчислення, 3D-візуалізацію та експериментальну 
реєстрацію даних у єдиному програмному середовищі, розширюючи можливості 
навчальних, дослідницьких і прикладних застосувань у сфері прикладної 
астрономії. 
Практичне значення отриманих результатів. Практичне значення 
розробки полягає у створенні сучасного та інтерактивного інструменту для 
навчання, досліджень та практичних застосувань у сфері оптичної астронавігації. 
Платформа дозволяє користувачам вимірювати орієнтацію пристрою, визначати 
місцевий зоряний час, відстежувати положення Полярної Зорі та досліджувати 
каталог зірок у реальному часі. 3D-візуалізація Землі з маркером реального 
місцезнаходження та анімоване відображення нахилу осі планети роблять процес 
дослідження наочним та зручним для користувачів. 
Отриманий комплекс може використовуватися як навчальний та 
дослідницький інструмент у освітніх закладах, наукових проєктах, а також для 
практичних задач у сфері астрономії та геопозиціювання, сприяючи підвищенню 
точності орієнтації та розвитку навичок роботи з сучасними оптичними методами 
астронавігації. 
Особистий внесок автора. Автор самостійно виконав повний цикл 
науково-прикладного дослідження, що включав розробку концепції та 
архітектури програмної платформи для комплексу оптичної астронавігації. 
Зокрема, особистий внесок автора полягає у наступному: 
У сфері системного аналізу та архітектури ПЗ: обґрунтовано та реалізовано 
об’єктно-орієнтовану модель системи на мові Kotlin (платформа Android), що 
забезпечує стабільну інтеграцію низькорівневих апаратних засобів із 
високорівневими обчислювальними алгоритмами. 
10 
ЧДТУ 252477.002 ПЗ 
У сфері опрацювання сигналів та сенсорики: розроблено та імплементовано 
алгоритми обробки даних інерціальних сенсорів (акселерометра, гіроскопа та 
магнітометра). Автор адаптував методи фільтрації шумів та вирішив задачу 
визначення просторової орієнтації пристрою в реальному часі. 
У сфері астрономічних обчислень: проведено математичний синтез 
алгоритмів для обчислення фундаментальних параметрів - Юліанської дати (JD), 
гринвіцького та місцевого зоряного часу (GMST/LST), а також перетворення 
екваторіальних координат об'єктів у горизонтальну систему координат з 
урахуванням геоданих користувача. 
У сфері управління даними та візуалізації: спроєктовано структуру 
реляційної бази даних SQLite для оптимізації доступу до масивів астрономічних 
об’єктів. Автор розробив математичну модель 3D-візуалізації геоїда Землі 
засобами OpenGL ES, реалізувавши алгоритми проекції сферичних координат на 
площину графічного конвеєра. 
У сфері реєстрації експерименту: розроблено оригінальний модуль 
інтелектуальної реєстрації фотометричних даних. Автор науково обґрунтував та 
технічно реалізував метод синхронної фіксації RAW-зображень (DNG) із 
супровідними гетерогенними метаданими (сенсорні вектори, часові та 
астрономічні мітки), що створює базу для формування верифікованих датасетів. 
У сфері валідації результатів: автором проведено комплексне тестування 
розробленого ПЗ, що дозволило оцінити точність позиціонування та стабільність 
системи в динамічних умовах експлуатації. 
  
11 
ЧДТУ 252477.002 ПЗ 
РОЗДІЛ 1 ІСНУЮЧУ МЕТОДИ ТА ЗАСОБИ РОЗВ’ЯЗАННЯ 
ПОСТАВЛЕНИХ ЗАДАЧ 
У цьому розділі проводиться аналіз наукової та технічної інформації, що 
стосується розробки програмної платформи комплексу оптичної астронавігації. 
Розглядаються існуючі методи визначення орієнтації пристроїв, роботи з 
сенсорами (акселерометр, гіроскоп, магнітометр), обчислення місцевого зоряного 
часу та геопозиціювання за допомогою GPS. Аналізуються доступна література, 
сучасні мобільні технології, бібліотеки для 3D-візуалізації та обробки сенсорних 
даних, а також існуючі програмні рішення для астрономічних обчислень. 
Завданням розділу є визначення основних вимог до функціоналу 
платформи, обґрунтування вибору технологій для реалізації обчислень, обробки 
даних сенсорів та інтерактивної 3D-візуалізації, а також виділення проблемних 
аспектів та шляхів їх ефективного вирішення. 
Загальні відомості щодо понять зоряного часу та небесної сфери були 
отримані з відкритих енциклопедичних джерел [6]. Оформлення пояснювальної 
записки виконано відповідно до методичних рекомендацій [7]. 
1.1 Аналіз існуючих рішень 
Stellarium [14] є популярним астрономічним додатком, доступним на 
платформі Android, що дозволяє користувачам досліджувати зоряне небо у 
реальному часі. Програма демонструє інтерактивну візуалізацію небесної сфери, 
надає можливість перегляду зірок, планет та сузір’їв, а також обчислює 
астрономічні параметри, такі як зоряний час та положення об’єктів. 
Додаток використовує GPS для визначення геопозиції користувача та 
орієнтації пристрою, а також підтримує інтерактивну зміну масштабу і руху 
огляду. Stellarium для Android є зручним інструментом для навчання та 
досліджень, але він не інтегрує одночасно локальний каталог зірок з можливістю 
пошуку, обробку даних акселерометра, гіроскопа та магнітометра, а також 3D- 
візуалізацію Землі з маркером реального місцезнаходження користувача. 
12 
ЧДТУ 252477.002 ПЗ 
Плюси Stellarium: 
1 Реалістична 3D-візуалізація неба: Відображає зірки, планети, сузір’я 
та інші небесні об’єкти у реальному часі. 
2 Використання GPS: Дозволяє автоматично визначати 
місцезнаходження користувача для точного відображення небесних 
об’єктів. 
3 Інтерактивність: Підтримка масштабування, обертання огляду та руху 
по небесній сфері. 
4 Освітній потенціал: Надання детальної інформації про зірки, планети 
та астрономічні події. 
5 Безкоштовність: Додаток доступний безкоштовно, що робить його 
популярним серед студентів і любителів астрономії. 
Мінуси Stellarium: 
1 Обмежена інтеграція сенсорів: Не використовує повністю 
акселерометр, гіроскоп і магнітометр для обчислень азимута та 
орієнтації пристрою. 
2 Відсутність локального каталогу зірок з пошуком: Не підтримує 
швидкий доступ до зірок за характеристиками (RA, Dec, тип). 
Відсутність 3D-моделі Землі з маркером користувача: Не показує 3D 
відображення планети з конкретним місцезнаходженням користувача. 
3 Відсутність інтеграції з Python чи іншими обчислювальними 
скриптами: Обмежує можливості для складних астрономічних 
розрахунків у реальному часі. 
4 Продуктивність на старих пристроях: На деяких смартфонах 3D-
візуалізація може працювати повільно. 
SkySafari [14] - це потужний астрономічний додаток для платформи 
Android, призначений для спостереження за небесними об’єктами та 
планетарними системами. Програма дозволяє користувачам переглядати зірки, 
13 
ЧДТУ 252477.002 ПЗ 
планети, сузір’я та комети у реальному часі, обчислює зоряний час, положення 
небесних тіл і їх рухи, а також забезпечує детальну інформацію про астрономічні 
події. 
 
 
Рисунок 1.1 - Інтерфейс Stellarium  
 
Додаток використовує GPS для визначення місцезнаходження користувача і 
дозволяє орієнтуватися у просторі за допомогою сенсорів пристрою. SkySafari 
підтримує інтерактивну 3D-візуалізацію неба та масштабування, що робить 
14 
ЧДТУ 252477.002 ПЗ 
 спостереження більш наочним і зручним для користувача. 
Разом із тим, SkySafari не надає повного інтегрованого функціоналу для 
локального каталогу зірок із пошуком, не включає детальну обробку даних 
акселерометра, гіроскопа та магнітометра, а також не реалізує 3D-модель Землі з 
маркером реального місцезнаходження користувача. Це підкреслює необхідність 
створення власної платформи оптичної астронавігації, яка об’єднує всі ці функції  
в одному мобільному додатку для навчання, досліджень та практичного 
використання. 
Плюси SkySafari: 
1 Детальна база даних астрономічних об’єктів: Містить мільйони зірок, 
планет, комет, супутників та інших небесних тіл. 
2 Реалістична 3D-візуалізація неба: Підтримує інтерактивний огляд, 
масштабування та обертання. 
3 GPS та сенсори: Використовує геопозицію користувача для точного 
відображення неба і орієнтації. 
4 Астрономічні розрахунки: Обчислює зоряний час, координати 
небесних об’єктів, фази Місяця та планетарні позиції. 
5 Освітні та навчальні матеріали: Дає докладні описові дані про об’єкти 
та події у небі, що зручно для навчання. 
6 Підтримка телескопів: Може керувати сумісними телескопами, що 
дозволяє практичне спостереження. 
Мінуси SkySafari: 
1 Частково платний функціонал: Багато розширених функцій доступні 
лише у Pro-версії. 
2 Обмежена локальна робота з даними: Відсутня можливість створення 
кастомного локального каталогу зірок з пошуком. 
3 Відсутність 3D-моделі Землі з маркером місцезнаходження 
користувача: Додаток не показує тривимірну модель планети для 
навігації по земній поверхні. 
4 Мало інтеграції з Python або додатковими обчислювальними  
15 
ЧДТУ 252477.002 ПЗ 
скриптами: Не дозволяє виконувати складні розрахунки прямо в 
додатку. 
5 Продуктивність: На старих або малопотужних пристроях можуть 
виникати затримки при відображенні великої кількості об’єктів у 3D. 
Аналіз існуючих мобільних рішень проводився на основі доступних 
додатків у Google Play [11]. 
 
 
Рисунок 1.2 - Інтерфейс SkySafari  
 
1.2 Існуючі методи та засоби 
Для розв’язання поставлених завдань у створенні комплексу оптичної 
астронавігації використовуються такі методи та засоби: 
1 Kotlin та Android SDK: Використовуються для розробки мобільного 
додатку на платформі Android, що забезпечує інтерактивний інтерфейс 
та роботу з сенсорами пристрою. Компонентний підхід дозволяє 
спростити підтримку та масштабування функціоналу. 
16 
ЧДТУ 252477.002 ПЗ 
2 Обробка даних сенсорів: Методи зчитування та обробки даних 
акселерометра, гіроскопа і магнітометра застосовуються для визначення 
орієнтації пристрою, кута нахилу та азимута. 
3 GPS та геопозиціювання: Використовується для отримання координат 
користувача та обчислення астрономічних параметрів, таких як місцевий 
зоряний час (LST) та Юліанська дата (JD). 
4 База даних SQLite: Використовується для зберігання локального 
каталогу зірок, забезпечуючи швидкий доступ до інформації та 
можливість пошуку зірок. 
5 Інтеграція Python : Дозволяє розширити аналітичні та обчислювальні  
6 можливості додатку, виконуючи складні обчислення та сценарії 
всередині Android-додатку. 
7 3D-візуалізація через OpenGL ES 2.0: Забезпечує відображення 
тривимірної моделі Землі з маркером місцезнаходження користувача, 
підтримку жестів масштабування та обертання, а також анімоване 
відображення нахилу осі планети. 
 
 
Рисунок 1.3 - Використання  SQLite 
 
 
Рисунок 1.4 - Використання 3D-візуалізації 
17 
ЧДТУ 252477.002 ПЗ 
1.3 Актуальні проблеми 
При розробці комплексу оптичної астронавігації виникають такі актуальні 
проблеми: 
1 Точність сенсорних даних: Необхідність коректної обробки даних 
акселерометра, гіроскопа та магнітометра для забезпечення правильної 
орієнтації та визначення азимута. 
2 Обчислення астрономічних параметрів: Забезпечення точності 
розрахунків місцевого зоряного часу (LST), Юліанської дати (JD) та 
напрямку на Полярну Зорю. 
3 Продуктивність і швидкодія: Оптимізація роботи додатку при обробці 
сенсорних даних, виконанні обчислень та візуалізації 3D-моделі Землі. 
4 Зручність користування: Створення інтуїтивно зрозумілого 
інтерфейсу, який дозволяє користувачам легко орієнтуватися у додатку 
та взаємодіяти з сенсорними даними, каталогом зірок і 3D-візуалізацією. 
5 Інтеграція різних модулів: Забезпечення стабільної роботи всіх 
компонентів додатку - сенсорів, обчислень, бази даних і 3D-візуалізації - 
у єдиній системі. 
1.4 Методи вирішення проблем 
Для вирішення актуальних проблем у розробці комплексу оптичної 
астронавігації застосовуються наступні методи: 
1 Використання сучасних технологій: Застосування Kotlin та Android 
SDK, бібліотек для роботи з сенсорами та OpenGL ES 2.0 для 3D-
візуалізації дозволяє ефективно обробляти дані та забезпечує швидкий 
доступ до обчислених астрономічних параметрів. 
2 Оптимізація обчислень та обробки даних: Використання ефективних 
алгоритмів для роботи з даними акселерометра, гіроскопа та 
магнітометра, оптимізація обчислень LST та координат, а також 
оптимізація роботи бази даних SQLite для каталогу зірок. 
3 Юзабіліті-тестування: Проведення тестувань інтерфейсу та 3D-
візуалізації для забезпечення зручності використання додатку, 
18 
ЧДТУ 252477.002 ПЗ 
інтуїтивного керування сенсорними даними, навігацією по каталогу 
зірок та взаємодії з маркером місцезнаходження. 
4 Інтеграція модулів: Забезпечення стабільної взаємодії між сенсорними 
даними, астрономічними обчисленнями, базою даних і 3D-візуалізацією 
для єдиного, надійного та зручного мобільного інструменту. 
1.5 Систематизація отриманих даних 
Для успішної реалізації платформи оптичної астронавігації необхідно 
систематизувати отримані дані з різних джерел. Це включає: 
1 Аналіз наукових та технічних статей, підручників та монографій з 
астрономії та навігаційних технологій: Глибоке вивчення літератури 
дозволяє зрозуміти сучасний стан досліджень у галузі оптичної 
астронавігації, методів обробки сенсорних даних, обчислення зоряного 
часу, 3D-візуалізації та інтеграції з GPS. 
2 Вивчення популярних онлайн-ресурсів і мобільних додатків для 
астрономії: Огляд форумів, блогів, статей та відео допомагає 
ознайомитися з практичними аспектами використання мобільних 
астрономічних додатків, такими як Stellarium та SkySafari, та дізнатися 
про новітні технології і підходи до інтерактивної візуалізації та обробки 
сенсорних даних. 
3 Огляд існуючих рішень на ринку: Дослідження додатків на Android 
для астрономії, таких як Stellarium і SkySafari, дозволяє визначити їхні 
ключові функції - від 3D-візуалізації неба та планет до роботи з GPS та 
сенсорами - і зрозуміти, які функції користувачі вважають найбільш 
корисними. 
4 Проведення порівняльного аналізу: Порівняння різних мобільних 
астрономічних рішень допомагає виявити їхні переваги та недоліки, а 
також визначити, які функції можна адаптувати або покращити у 
власному проєкті. Це включає оцінку точності обчислень, зручності 
користування, продуктивності та інтеграції сенсорних даних. 
5 Збір зворотного зв’язку від користувачів: Аналіз відгуків користувачі 
19 
ЧДТУ 252477.002 ПЗ 
в мобільних астрономічних додатків дозволяє визначити, які функції є 
критично важливими, а які аспекти інтерфейсу або точності потребують 
вдосконалення. 
6 Використання методів дизайн-мислення: Визначення проблем, 
генерації ідей, прототипування та тестування допомагає глибше 
зрозуміти потреби користувачів та знайти інноваційні рішення для 
інтеграції сенсорів, обчислень та 3D-візуалізації. 
Отже, аналіз існуючих методів та рішень показує, що для створення 
ефективного та наочної платформи оптичної астронавігації необхідно 
використовувати сучасні технології роботи з сенсорами, бази даних, а також 
інструменти для 3D-візуалізації. Вивчення аналогів, таких як Stellarium і 
SkySafari, дозволяє визначити ключові функції та вимоги до системи, що сприяє 
розробці високоякісного мобільного додатку для навчання, досліджень та 
практичних астрономічних спостережень. 
1.6 Постановка задачі досліджень 
Сформульована мета досягається шляхом послідовного розв’язання, що 
передбачає розв’язання задач класифікації небесних тіл, ідентифікації 
функціональних залежностей результатів визначення місця положення від ознак 
отриманих з сенсорів та обчислень.  
Для досягнення мети було визначено та розв’язано наступні задачі: 
1 Інтерфейс користувача: Розробити зручний та інтуїтивно зрозумілий 
інтерфейс, який дозволяє користувачам взаємодіяти з сенсорними 
даними, 3D-візуалізацією та каталогом зірок. 
2 Робота з сенсорами: Забезпечити обробку даних акселерометра, 
гіроскопа та магнітометра для визначення орієнтації пристрою, кутів 
нахилу та азимута. 
3 Астрономічні обчислення: Реалізувати розрахунок місцевого зоряного 
часу (LST), Юліанської дати (JD) та напрямку на Полярну Зорю. 
4 Каталог зірок: Створити локальну базу даних SQLite для зберігання 
інформації   про   зірки   з   можливістю   пошуку   за   назвою,   прямим  
20 
ЧДТУ 252477.002 ПЗ 
сходженням, схиленням та типом зірки. 
5 3D-візуалізація: Розробити тривимірну модель Землі з можливістю 
встановлення маркера місцезнаходження користувача, а також 
підтримкою жестів обертання та масштабування. 
6 Візуалізація нахилу: Створити інтерактивний елемент інтерфейсу для 
анімованого відображення кута нахилу осі Землі. 
7 Тестування та оптимізація: Провести комплексне тестування та 
оптимізацію платформи для підвищення точності обчислень, 
продуктивності додатку та зручності користування. 
8 Модуль камери та обробка експериментальних даних: 
Реалізувати модуль серійної фотозйомки у форматі RAW з фіксованим 
часовим інтервалом, забезпечити збереження зображень разом із 
супровідними метаданими, що містять сенсорні покази, часові мітки, 
географічні координати та обчислені астрономічні параметри. 
  
21 
ЧДТУ 252477.002 ПЗ 
ВИСНОВОК ДО РОЗДІЛУ 1 
У даному розділі проведено аналіз наукової та технічної інформації, що 
стосується розробки програмної платформи комплексу оптичної астронавігації. 
Були розглянуті існуючі мобільні астрономічні додатки, такі як Stellarium та 
SkySafari, які забезпечують користувачів детальною інформацією про небесні 
об’єкти та демонструють сучасні методи візуалізації неба. 
За допомогою сучасних технологій, таких як Kotlin для розробки 
мобільного додатку, OpenGL ES для 3D-візуалізації, SQLite для локальної бази 
даних та для інтеграції Python-скриптів, планується створення зручної та 
функціональної платформи, яка дозволяє обробляти дані сенсорів, обчислювати 
місцевий зоряний час, визначати координати та напрямок на Полярну Зорю, а 
також відображати тривимірну модель Землі з маркером місцезнаходження 
користувача. 
У розділі також ідентифіковано ключові проблеми, пов’язані з точністю 
сенсорних вимірювань, інтеграцією різнорідних даних, продуктивністю 3D-
візуалізації та часовою синхронізацією. Особливу увагу приділено питанням 
узгодження сенсорних даних із результатами серійної фотозйомки, що є 
важливим аспектом для формування експериментальних наборів оптичних даних 
з коректною часовою та просторовою прив’язкою. 
Загальний аналіз існуючих методів і програмних засобів показав, що 
ефективна реалізація комплексу оптичної астронавігації потребує поєднання 
сучасних технологій мобільної розробки, сенсорної обробки, 3D-візуалізації та 
керування камерою, а також врахування досвіду вже наявних астрономічних 
додатків. Це створює передумови для побудови функціональної, точної та 
надійної платформи, орієнтованої як на навчальні, так і на дослідницькі задачі. 
  
22 
ЧДТУ 252477.002 ПЗ 
РОЗДІЛ 2 ТЕОРЕТИЧНІ ТА ЕКСПЕРИМЕНТАЛЬНІ ДОСЛІДЖЕННЯ 
2.1 Теоретичні дослідження 
У даному розділі проведено синтез та аналіз математичних моделей 
обробки сигналів інерціальних сенсорів, алгоритмів сферичної астрономії та 
методів реєстрації мультимодальних даних. Дослідження спрямоване на 
розв’язання задачі високоточного визначення просторової орієнтації та 
формування верифікованих наборів експериментальних даних. 
2.1.1 Формалізація задачі дослідження 
Об’єктом дослідження є процес інтеграції апаратних потужностей мобільної 
платформи (сенсорна підсистема, модуль камери) з математичним апаратом 
астронавігації.  
Предметом дослідження є алгоритми синхронізації гетерогенних даних 
(зображення RAW, вектори прискорення, магнітна індукція, астрономічний час) у 
єдиному часовому та координатному просторі. 
2.1.2 Математичні моделі сенсорних вимірювань та орієнтації 
Відповідно коду: Реалізовано в AccelerometerActivity.kt, 
MagnetometerActivity.kt. В роботі застосовано модель визначення орієнтації на 
основі розв'язання системи тригонометричних рівнянь для нормалізованих 
векторів гравітації а та магнітного поля m:  
 
(2.1) 
 
 
Розрахункова модель кута поперечного нахилу (Roll) відносно поздовжньої 
осі пристрою, де: 
 Roll - кут поперечного нахилу пристрою (нахил вліво-вправо, навколо 
осі Y). 
 arctan2 (або atan2 в коді) - функція арктангенса з двома аргументами.  
23 
ЧДТУ 252477.002 ПЗ 
Вона повертає кут, враховуючи знаки обох координат (y та z), що 
о  о
дозволяє визначити кут у повному діапазоні (-180 до +180 ), на відміну 
від звичайного arctan. 
 ax- прискорення по осі X (поперечна вісь). Це основний компонент, що 
визначає крен. 
     
     - знаменник, що являє собою проекцію вектора гравітації на 
площину YZ. Це потрібно для того, щоб кут Roll розраховувався 
коректно незалежно від того, наскільки сильно телефон нахилений 
вперед або назад (Pitch). 
 
(2.2) 
 
 
Математичний вираз для обчислення кута поздовжнього нахилу (Pitch) 
відносно горизонту, де: 
 Pitch - кут поздовжнього нахилу пристрою (нахил вперед-назад, навколо 
осі X). Вимірюється в радіанах або градусах. 
 arctan2 (або atan2 в коді) - функція арктангенса з двома аргументами. 
Вона повертає кут, враховуючи знаки обох координат (y та z), що 
о  о
дозволяє визначити кут у повному діапазоні (-180 до +180 ), на відміну 
від звичайного arctan. 
 ay - показник акселерометра по осі Y. Ця вісь проходить вздовж довгої 
сторони екрана смартфона (вгору-вниз). 
 az - показник акселерометра по осі Z. Ця вісь проходить 
перпендикулярно до екрана (вперед-назад). 
 ax (у повній формулі) - показник акселерометра по осі X. Ця вісь 
проходить поперек екрана (вправо-вліво). 
Зазначені моделі імплементовані в модулі AccelerometerActivity, де функція 
о
atan2   забезпечує   коректне   визначення   квадранта   кута   в  діапазоні  180 ,  що  
24 
ЧДТУ 252477.002 ПЗ 
критично для стабільної візуалізації нахилу Землі в компоненті TiltView. 
Дослідження: Проведено аналіз похибок (шум, дрейф) через метод 
onAccuracyChanged. Виявлено, що стабільність азимута критично залежить від 
статусу SENSOR_STATUS_ACCURACY_HIGH. Принципи об’єднання даних 
інерціальних сенсорів базуються на концепціях sensor fusion, описаних у 
технічній документації STMicroelectronics [13, 14]. 
2.1.3 Математичні моделі астрономічного часу та навігації 
Відповідність коду: Об'єкт AstroCalculator.kt, методи julianDate та lstHours. 
Для забезпечення часової прив’язки розроблено алгоритм обчислення місцевого 
зоряного часу (LST) через Юліанську дату (JD) та гринвіцький середній зоряний 
час (GMST). 
(2.3) 
 
 
Алгоритм обчислення зоряного часу, де: 
 LST (Місцевий зоряний час) -вимірюється в годинах (0…24) і вказує 
на пряме сходження (RA) об'єктів, які в даний момент знаходяться точно 
на меридіані спостерігача. 
 GMST (Середній зоряний час за Гринвічем) - це положення точки 
весняного рівнодення відносно нульового (Гринвіцького) меридіана. 
 lambda (Довгота) - географічна довгота спостерігача.  
Це дозволяє реалізувати перехід від об’єктно-центрованих координат 
каталогу (RA, Dec) до спостерігач-центрованих (Az, Alt). Алгоритми обчислення 
Юліанської дати та зоряного часу базуються на класичних астрономічних 
методах, описаних у роботах NASA та Meeus [8, 12]. Практичні аспекти 
астрономічних розрахунків орієнтації описані у класичних роботах з прикладної 
астрономії [13]. 
2.1.4 Модель програмного комплексу 
Програмний   комплекс   представлено   як   п'ятикомпонентну   ієрархічну  
25 
ЧДТУ 252477.002 ПЗ 
структуру: 
1 Рівень збору даних: (API Camera2, SensorManager). 
2 Обчислювальне ядро: (AstroCalculator, StarDbHelper). 
3 Рівень зберігання: (SQLite для зірок, JSON/DNG для експериментів). 
4 Рівень візуалізації: (Відтворення Землі OpenGL ES в EarthRenderer.kt). 
5 UI/UX Layer: (Адаптивні активності на базі BaseActivity). 
Гіпотеза дослідження: Припускається, що інтеграція даних інерціальних 
сенсорів мобільного пристрою з алгоритмами обчислення місцевого зоряного 
часу та методами синхронної реєстрації метаданих дозволяє забезпечити 
визначення просторової орієнтації з точністю, достатньою для ідентифікації 
астрономічних об’єктів, та сформувати верифіковані мультимодальні набори 
даних для подальшої аналітичної обробки. 
2.2. Експериментальні дослідження 
У цьому підрозділі представлено результати апробації розробленого ПЗ. 
2.2.1 Експеримент №1. Валідація точності орієнтації та пошуку 
Полярної зорі 
Методика: Порівняння розрахованого азимута в PolarisFinderActivity.kt із 
контрольними вимірюваннями. 
о
Реалізація в коді: Метод activateGlow() спрацьовує при дельті < 10 . 
Результат: середньоквадратичне відхилення (СкВ) на відкритій місцевості 
о
склало 2.4 . Підтверджено ефективність алгоритму візуального підтвердження . 
2.2.2 Експеримент №2. Оцінка продуктивності графічної підсистеми 
3D-візуалізації 
Методика: Вимірювання часу рендерингу кадру в EarthGLSurfaceView.kt. 
Результат: при обробці жестів масштабування (scaleDetector) та обертання 
частота оновлення кадрів залишалася стабільною (60+ FPS), що підтверджує 
ефективність використання шейдерів vsSphereShader. 
2.2.3 Експеримент №3. Формування мультимодального датасету 
Методика. Серійна зйомка з кроком N секунд. 
26 
ЧДТУ 252477.002 ПЗ 
Реалізація в коді: MainActivity.kt, метод captureStillPicture. 
Процес: 
1 Захоплення RAW-кадру (DngCreator). 
2 Синхронне зчитування масивів accel, gyro, mag. 
3 Запис у JSONObject. 
Результат. Сформовано набір даних, де кожен DNG-файл має ідентичну 
часову мітку з JSON-файлом метаданих. Це дозволяє відновити орієнтацію 
камери на небесній сфері з точністю до 1 секунди дуги (теоретично). 
 
 
Рис 2.1 - Діаграми послідовності 3 експерименту 
27 
ЧДТУ 252477.002 ПЗ 
Реалізація серійної RAW-фотозйомки виконувалась із використанням 
Camera2 API [16]. 
2.2.4 Експеримент №4. Аналіз латентності бази даних 
Методика. Запит до StarDbHelper при batchSize = 200,000. 
Результат. Використання Dispatchers.IO (корутини) дозволило уникнути 
 блокування UI-потоку, час вибірки становить < 150 мс. 
28 
ЧДТУ 252477.002 ПЗ 
ВИСНОВОК ДО РОЗДІЛУ 2 
У другому розділі виконано теоретичні та експериментальні дослідження, 
спрямовані на обґрунтування можливості використання мобільного пристрою як 
платформи для задач оптичної астронавігації та збору мультимодальних 
експериментальних даних. 
У ході теоретичного аналізу сформалізовано задачу інтеграції інерціальних 
сенсорів, модуля камери та алгоритмів сферичної астрономії в єдиному часовому 
й координатному просторі. Обґрунтовано використання математичних моделей 
орієнтації на основі векторів гравітації та магнітного поля, а також показано 
вплив шумів, дрейфу та точності калібрування сенсорів на стабільність 
визначення азимуту та просторової орієнтації. 
Розглянуто алгоритми обчислення Юліанської дати та місцевого зоряного 
часу, що забезпечують коректний перехід між екваторіальною та горизонтальною 
системами координат і дозволяють поєднати сенсорні вимірювання з 
астрономічними розрахунками. Запропонована модель програмного комплексу з 
багаторівневою архітектурою забезпечує модульність, масштабованість і 
стабільну роботу системи. 
Експериментальні дослідження підтвердили працездатність розробленого 
програмного забезпечення. Отримано достатню точність визначення напрямку на 
Полярну зорю, стабільну продуктивність підсистеми 3D-візуалізації та ефективну 
роботу локальної бази даних зірок. Ключовим результатом є реалізація механізму 
серійної RAW-фотозйомки з синхронним записом сенсорних, геопозиційних та 
астрономічних метаданих, що дозволяє формувати верифіковані мультимодальні 
набори даних. 
Загалом результати розділу підтверджують доцільність обраних 
математичних моделей, алгоритмічних рішень і програмної архітектури та 
створюють основу для подальшого розвитку системи і практичного використання 
в задачах аматорської та прикладної астронавігації. 
  
29 
ЧДТУ 252477.002 ПЗ 
РОЗДІЛ 3 ВПРОВАДЖЕННЯ РЕЗУЛЬТАТІВ ДОСЛІДЖЕНЬ У 
ПРАКТИКУ ПРОЕКТУВАННЯ ПРОГРАМНОГО ЗАБЕЗПЕЧЕННЯ 
ІНФОРМАЦІЙНИХ СИСТЕМ 
У сучасному світі технологій астрономія та космічна навігація активно 
розвиваються, зокрема завдяки мобільним додаткам та інтерактивним 
платформам, що дозволяють здійснювати спостереження за небесними об’єктами 
та виконувати точні астрономічні розрахунки. Користувачі шукають ефективні та 
наочні інструменти для вивчення неба, орієнтації у просторі та аналізу 
астрономічних даних, тоді як розробники створюють технологічні рішення для 
інтеграції сенсорів, обчислень та візуалізації. 
Метою цього проєкту є розробка програмної платформи комплексу 
оптичної астронавігації для мобільних пристроїв на платформі Android, яка 
забезпечить користувачів інструментами для точного визначення орієнтації 
пристрою, обчислення місцевого зоряного часу, відображення напрямку на 
Полярну Зорю та інтерактивної візуалізації Землі з маркером місцезнаходження 
користувача. 
У цьому розділі буде описано процес впровадження результатів досліджень 
у практику розробки програмного забезпечення астрономічних інформаційних 
систем. Розглянуто основні етапи реалізації платформи, вибрані технології та 
методи роботи з сенсорами, обчислень та 3D-візуалізації, а також окреслено 
перспективи подальшого розвитку платформи та можливості її вдосконалення для 
навчальних та наукових цілей. 
3.1 Модель предметної області 
3.1.1 Предметна область моделювання. Модель предметної області. Словник 
предметної області 
Предметна область моделювання  
Предметна область моделювання охоплює процес автономної орієнтації 
мобільного пристрою у просторі з використанням небесних орієнтирів та 
інерціальних вимірювань. 
30 
ЧДТУ 252477.002 ПЗ 
Основні компоненти ПОМ включають: 
Небесна сфера: сукупність астрономічних об'єктів із їхніми координатами 
RA, Dec), що зберігаються у локальному каталозі SQLite. 
Геопросторове середовище: координати спостерігача (GPS) та часові 
параметри (UTC, Юліанська дата, зоряний час LST), які визначають положення 
Землі відносно зірок. 
Фізичний стан пристрою: вектори прискорення та магнітного поля, що 
зчитуються сенсорами смартфона для розв'язання зворотної задачі орієнтації 
(визначення Pitch, Roll, Azimuth). 
Оптико-цифрова реєстрація: процес фіксації зоряного неба через камеру 
пристрою (Camera2 API) з одночасним «прив’язуванням» фізичних метаданих до 
кожного кадру. 
Моделювання предметної області розробленої програмної платформи 
базується на об’єктно-орієнтованому підході, реалізованому мовою Kotlin в 
середовищі Android. Система проєктується як сукупність взаємодіючих 
компонентів: сенсорної підсистеми, модуля астрономічних розрахунків, бази 
даних небесних тіл та підсистеми візуалізації. 
Центральне місце в моделі займає взаємодія між фізичними даними, 
отриманими від апаратного забезпечення пристрою (сенсорів та GPS), та 
математичними алгоритмами розрахунку часових і просторових параметрів (LST, 
азимут). Архітектура програми побудована навколо базового класу BaseActivity, 
що забезпечує єдиний контекст виконання, та спеціалізованих класів-активностей 
для кожної предметної сутності. 
Модель предметної області представлена на рисунку 3.1, вона візуалізує 
графічне відображення предметної області та демонструє взаємодію між 
ключовими сутностями системи, 
 
Таблиця 3.1 
Словник предметної області 
31 
ЧДТУ 252477.002 ПЗ 
Категорія Термін Опис / Значення у проекті 
Місцевий зоряний час; використовується для 
LST (Local 
Астрономія синхронізації карти неба з меридіаном 
Sidereal Time) 
спостерігача. 
Юліанська дата; універсальний формат часу для 
Астрономія JD (Julian Date) 
астрономічних розрахунків відносно епохи J2000. 
Пряме сходження та Схилення; екваторіальні 
Астрономія RA / Dec 
координати зірок у небесній сфері. 
Кут нахилу пристрою вперед-назад, що 
Сенсори Pitch (Тангаж) 
визначається акселерометром. 
Сенсори Roll Кут нахилу пристрою вліво-вправо. 
Azimuth Кут орієнтації на Північ, що розраховується через 
Сенсори 
(Азимут) магнітометр. 
Алгоритм поєднання даних інерціальних датчиків 
Сенсори Sensor Fusion 
для зменшення похибки вимірювань. 
Розробка Інтерфейс Android для програмного керування 
Camera2 API 
ПЗ камерою та отримання RAW-даних. 
Розробка Формат цифрових негативів, що містить 
RAW (DNG) 
ПЗ необроблені дані з матриці для наукового аналізу. 
Розробка Структурований формат зберігання даних про 
JSON Metadata 
ПЗ стан сенсорів у момент зйомки. 
Розробка Локальна база даних, в якій зберігається каталог 
SQLite 
ПЗ зірок для автономної роботи. 
Розробка Графічний стандарт, використаний для 3D-
OpenGL ES 
ПЗ візуалізації земної кулі в додатку. 
 
3.1.2 Елементи моделювання  
На основі аналізу програмного коду визначено наступні ключові сутності, 
що являють собою елементи моделі: 
32 
ЧДТУ 252477.002 ПЗ 
1. Сенсорна Підсистема (SensorData) 
1 Реалізація в коді: Інтерфейс SensorEventListener, клас SensorManager. 
2 Атрибути: 
 Тип джерела: Sensor.TYPE_ACCELEROMETER (акселерометр), 
Sensor.TYPE_MAGNETIC_FIELD (магнітометр), 
Sensor.TYPE_GYROSCOPE (гіроскоп). 
 Сирі дані: Масиви значень FloatArray (x, y, z). 
 Похідні дані: Матриця обертання (rotationMatrix), орієнтація 
пристрою. 
3 Опис: Відповідає за зчитування фізичних показників руху та положення 
пристрою. Використовується класами PolarisFinderActivity та 
MagnetometerActivity для розрахунку азимута та візуального наведення 
на об'єкти. 
2. Зоряний Об’єкт (Star) 
1 Реалізація в коді: Клас даних Star (Data Class), база даних StarDbHelper. 
2 Атрибути: 
 name (Назва зірки); 
 ra (Пряме сходження); 
 dec (Схилення); 
 type (Спектральний тип або клас об'єкта). 
3 Опис: Представляє фундаментальну одиницю астрономічного каталогу. 
Дані завантажуються з JSON-файлу у локальну базу даних SQLite для 
подальшого пошуку та відображення у списку RecyclerView. 
3. Геопросторове Положення (Observer) 
1 Реалізація в коді: LocationServices, FusedLocationProviderClient. 
2 Атрибути: 
 latitude (Широта); 
 longitude (Довгота); 
33 
ЧДТУ 252477.002 ПЗ 
 time (Час отримання фіксації). 
3 Опис: Сутність, що визначає географічні координати спостерігача. 
Отримується через Google Location Services API у класі 
TimeSiderealActivity і є критично важливою для перерахунку 
універсального часу в локальний зоряний час. 
4. Астрономічний Обчислювач (AstroCalculator) 
1 Реалізація в коді: Методи julianDate(), gmstHours(), obliquityAngle() у 
класі TimeSiderealActivity. 
2 Атрибути: 
 JD (Юліанська дата); 
 LST (Місцевий зоряний час); 
 Azimuth (Розрахунковий кут на Полярну зорю). 
3 Опис: Набір математичних алгоритмів, що перетворюють вхідні дані 
(час, GPS, дані сенсорів) у навігаційні параметри. Наприклад, формула  
LST = (GMST + longitude / 15.0) % 24.0 моделює обертання небесної 
сфери для конкретного спостерігача. 
5. Інтерактивна Візуалізація (EarthRenderer, StarDisplay) 
1 Реалізація в коді: Класи EarthGLSurfaceView, EarthRenderer, TiltView. 
2 Атрибути: 
 Текстури та шейдери OpenGL ES 2.0; 
 Кут нахилу (tiltAngle); 
 Координати маркера на сфері. 
3 Опис: Забезпечує графічне представлення моделі предметної області. 
Включає рендеринг 3D-моделі Землі з накладеними маркерами 
координат користувача та анімацію інтерфейсних елементів (стрілка 
компаса, індикатор нахилу) для взаємодії з користувачем. 
Реалізовано графічне відображення предметної області (Рис 3.1). 
 
34 
ЧДТУ 252477.002 ПЗ 
 
Рисунок 3.1 - Модель предметної області 
 
3.1.3 Робоча область моделювання: Програмна платформа комплексу 
35 
ЧДТУ 252477.002 ПЗ 
оптичної астронавігації 
Сенсорна Підсистема: 
1 Зчитування даних з акселерометра, гіроскопа та магнітометра; 
2 Визначення кутів нахилу пристрою (Roll, Pitch) та орієнтації в просторі; 
3 Забезпечення даними для обчислення азимута та напрямку на Полярну 
зорю. 
Геопросторове положення: 
1 Отримання GPS-координат користувача (широта, 
довгота);Використання координат для перерахунку UTC у місцевий 
зоряний час (LST); 
2 Встановлення маркерів на 3D-моделі Землі відповідно до місця 
спостереження. 
Зоряний час та астрономічні розрахунки: 
1 Обчислення Юліанської Дати (JD), Гринвічського середнього зоряного 
часу (GMST) та місцевого зоряного часу (LST);Розрахунок азимута для 
наведення на Полярну зорю та інших небесних об’єктів; 
2 Використання даних сенсорів та GPS для коректної орієнтації. 
Каталог зірок: 
1 Відображення списку небесних об’єктів із локальної бази даних SQLite; 
2 Пошук зірок за назвою, RA, Dec або типом; 
3 Ліниве завантаження та інтерактивне відображення у списку 
RecyclerView. 
Інтерактивна візуалізація: 
1 3D-модель Землі з маркером місцезнаходження користувача; 
2 Анімація орієнтації планети, кута нахилу осі та інтерфейсних елементів 
(стрілка компаса, індикатор нахилу); 
3 Можливість масштабування та обертання моделі користувачем. 
3.2 Формування та аналіз вимог 
Формування та аналіз вимог для програмної платформи комплексу оптичної  
36 
ЧДТУ 252477.002 ПЗ 
астронавігації є ключовим етапом розробки, оскільки саме на цьому етапі 
визначаються потреби користувачів та основні функціональні й нефункціональні 
характеристики системи. У ході аналізу враховуються особливості використання 
сенсорів мобільного пристрою, таких як акселерометр, гіроскоп і магнітометр, а 
також необхідність коректного отримання геопозиційних даних через GPS для 
обчислення місцевого зоряного часу та визначення напрямку на Полярну зорю. 
Процес формування вимог передбачає описання всіх компонентів 
платформи: сенсорної підсистеми, модулю астрономічних розрахунків, 
локального каталогу зірок та підсистеми візуалізації. Функціональні вимоги 
включають точне зчитування даних сенсорів, обробку отриманих сигналів для 
обчислення кутів нахилу та азимуту, надання користувачеві інформації про 
небесні об’єкти, а також інтерактивне відображення 3D-моделі Землі з маркером 
місцезнаходження користувача. 
Нефункціональні вимоги охоплюють швидкодію платформи, надійність 
отриманих даних, зручність користувацького інтерфейсу, кросплатформенність та 
стабільність роботи в умовах обмежених ресурсів мобільного пристрою. Ретельне 
визначення цих вимог дозволяє забезпечити ефективну інтеграцію всіх 
компонентів системи та створити інструмент, який буде корисним для 
астрономічних спостережень і навчальних цілей. 
Результатом аналізу вимог є чітке розуміння того, як платформа повинна 
функціонувати, які завдання вирішувати, а також які критерії повинні бути 
дотримані для забезпечення точності, зручності та надійності роботи системи. Ці 
вимоги формують основу для подальшого проектування, розробки та тестування 
програмного забезпечення. Для побудови діаграм діяльності, класів та 
компонентів використовувався інструмент Mermaid [4]. 
3.2.1 Формування вимог до програмного забезпечення. Первинні і детальні 
вимоги. Вимоги замовника. Функціональні та нефункціональні вимоги 
Формування вимог до програмного забезпечення 
 Програмна платформа повинна коректно функціонувати на мобільних 
пристроях під керуванням Android 8 або новіше. Пристрої повинні мати сенсори: 
37 
ЧДТУ 252477.002 ПЗ 
акселерометр, гіроскоп, магнітометр, а також модуль GPS для отримання 
геопозиційних даних. Для забезпечення графічної частини необхідна підтримка 
OpenGL ES 2.0 або вище, а обсяг оперативної пам’яті пристрою має бути не 
менше 4 ГБ. 
Первинні і детальні вимоги 
Первинні вимоги 
 Система повинна забезпечувати інтерактивне відображення 3D-моделі 
Землі з маркером місцезнаходження користувача. Платформа має зчитувати дані з 
сенсорів і перетворювати їх у навігаційні параметри, такі як кути нахилу, азимут і 
місцевий зоряний час. Користувач повинен мати можливість шукати та 
переглядати інформацію про небесні об’єкти з локальної бази даних. 
Детальні вимоги 
 Платформа повинна забезпечувати точне обчислення астрономічних 
параметрів, таких як LST, GMST, азимут і положення Полярної зорі. 
Відображення інформації має бути адаптивним під різні роздільності екранів та 
підтримувати інтерактивні елементи, такі як обертання моделі Землі, 
масштабування та стрілка компаса. 
Функціональні та нефункціональні вимоги  
Функціональні вимоги 
 Реєстрація та авторизація користувача передбачають можливість зберігати 
налаштування користувача та персоналізовані маркери. Платформа повинна 
надавати доступ до каталогу небесних об’єктів, включаючи пошук за назвою та 
типом об’єкта. Система повинна забезпечувати обчислення та візуалізацію 
орієнтації на Полярну зорю, а також відображення актуального місцевого 
зоряного часу та координат користувача на інтерактивній моделі Землі. 
Нефункціональні вимоги 
 Безпека даних користувача повинна гарантувати конфіденційність та 
цілісність збережених налаштувань. Платформа має бути швидкодіючою та 
стабільною навіть на пристроях із середніми технічними характеристиками. 
Архітектура повинна дозволяти масштабування для інтеграції нових модулів та 
38 
ЧДТУ 252477.002 ПЗ 
оновлення бази даних небесних об’єктів. Інтерфейс повинен бути адаптивним для 
різних типів екранів і забезпечувати зручну навігацію та взаємодію користувача з 
3D-візуалізацією. 
Вимоги замовника 
 Платформа повинна мати інтуїтивно зрозумілий та привабливий інтерфейс, 
що забезпечує користувачам унікальний досвід взаємодії з астрономічними 
даними. Для обчислювальної та візуалізаційної частини потрібно застосувати 
сучасні алгоритми і технології обробки сенсорних даних та тривимірної графіки, 
що забезпечують точність розрахунків і інтерактивність відображення. 
Вимоги розробника 
Вимоги розробника визначають технологічну базу та стандарти інженерії, 
необхідні для успішної реалізації математичних алгоритмів та стабільної роботи 
інтегрованих модулів системи. Основним інструментарієм розробки обрано 
середовище Android Studio версії Flamingo або новіше, що забезпечує повну 
сумісність із сучасними плагінами автоматизації збірки Gradle та інструментами 
налагодження енергоспоживання пристрою. Програмування логіки обчислень та 
графічного інтерфейсу здійснюється мовою Kotlin версії 1.8 та вище, оскільки 
вона надає механізми безпечної роботи з пам’яттю та підтримує корутини для 
асинхронного виконання важких математичних розрахунків у класі 
AstroCalculator без блокування основного потоку інтерфейсу. 
Технологічна реалізація вимагає використання специфічних інтерфейсів 
програмування, серед яких ключовим є Camera2 API з підтримкою рівнів Level 3 
або Full, що дозволяє розробнику отримувати доступ до необроблених даних 
матриці у форматі RAW DNG для подальшої наукової верифікації зоряних 
об’єктів. Окремим технологічним завданням для розробника є впровадження 
методів Sensor Fusion для програмної фільтрації шумів інерціальних датчиків, що 
дозволяє досягти плавності роботи азимутального покажчика та коректного 
нахилу 3D-моделі Землі. 
Для ефективного управління каталогом небесних тіл розробник має 
спроектувати реляційну структуру бази даних SQLite з використанням індексів за 
39 
ЧДТУ 252477.002 ПЗ 
координатами для миттєвого пошуку об’єктів у реальному часі. Графічна частина 
реалізується через розробку та оптимізацію GLSL-шейдерів у стандарті OpenGL 
ES, які забезпечують динамічне відображення текстур планети та світлових 
ефектів. Важливою архітектурною вимогою є дотримання принципів Clean 
Architecture, що дозволяє ізолювати обчислювальне ядро від Android-платформи 
та проводити автономне модульне тестування точності розрахунків Юліанських  
дат та зоряного часу. 
3.2.2 Формування вимог за допомогою діаграми прецедентів  
Сформовано діаграму прецедентів для вимог та визначено основні 
прецеденти та їхні взаємодії (Рис 3.2).  
Актор: 
 Користувач - кінцевий користувач системи, який взаємодіє з програмною 
платформою для отримання навігаційних даних та візуалізації небесних об’єктів. 
Прецеденти: 
1 Дані сенсорів: користувач збирає дані з акселерометра, гіроскопа, 
магнітометра та GPS, які передаються для подальших розрахунків. 
2 Астрономічні параметри: користувач отримує обчислені значення 
Юліанської Дати, Місцевого Зоряного Часу та азимуту Полярної зірки. 
3 Каталог зірок: користувач взаємодіє з локальною базою даних SQLite, 
використовуючи модель зірок та завантажені JSON-файли для пошуку 
небесних об’єктів. 
4 3D Орієнтація: користувач переглядає 3D-візуалізацію Землі, 
відображення кута нахилу та напрямок на Полярну зірку. 
5 Налаштування платформи: користувач має можливість змінювати 
налаштування додатку та персоналізувати інтерфейс. 
6 Серійна фотозйомка: користувач ініціює процес зйомки, після чого 
додаток автоматично виконує збереження RAW-зображень та 
відповідних JSON-файлів із сенсорними, геопозиційними та 
астрономічними даними. 
Взаємодія: 
40 
ЧДТУ 252477.002 ПЗ 
Користувач збирає фізичні дані від сенсорів, які включають акселерометр, 
гіроскоп, магнітометр та GPS. Ці дані передаються до модулів обчислення 
астрономічних параметрів, де розраховуються Юліанська Дата, Місцевий Зоряний 
Час та азимут на Полярну зірку. Паралельно користувач працює з локальною 
базою даних зірок, використовуючи модель зірок і завантажені JSON-файли. Всі 
результати візуалізуються у 3D-інтерфейсі Землі та в додаткових індикаторах 
кута нахилу та напрямку на Полярну зірку.  
3.2.3 Проектування логічної структури програмного комплексу 
Проектування логічної структури програмного комплексу є ключовим 
етапом у розробці програмного забезпечення, зокрема програмної платформи 
комплексу оптичної астронавігації. На цьому етапі визначається архітектурна 
концепція системи, основні компоненти та їх взаємозв’язки, що забезпечують 
ефективну обробку даних сенсорів, астрономічних розрахунків та візуалізацію 
результатів. 
Метою процесу є створення логічної структури програмної платформи, яка 
забезпечує точність обчислень, швидкість обробки інформації та зручність 
користування. У процесі проектування визначаються ключові підсистеми: 
сенсорна підсистема для збирання фізичних даних, модуль астрономічних 
розрахунків для обчислення часових і просторових параметрів, база даних з 
інформацією про небесні тіла, а також підсистема інтерактивної візуалізації для 
відображення 3D-моделі Землі та положення користувача з маркером. 
Вступ до проектування логічної структури охоплює аналіз взаємодії між 
компонентами, розробку схем обробки даних та визначення логічних алгоритмів 
роботи системи. Це дозволяє створити ефективну та надійну платформу, яка надає 
користувачу інтуїтивно зрозумілий інтерфейс та точні навігаційні дані в 
реальному часі. 
 
41 
ЧДТУ 252477.002 ПЗ 
 
Рисунок 3.2 - Діаграма прецедентів 
 
3.2.3.1 Діаграми класів  
Діаграма класів (Рис. 3.3) відображає логічну структуру програмної 
платформи комплексу оптичної астронавігації. Абстрактний клас BaseActivity 
забезпечує спільний контекст виконання для всіх активностей, включаючи 
MainActivity, сенсорні активності та обчислювальні модулі. BaseActivity керує 
поточними координатами спостерігача, забезпечує завантаження та збереження 
геопозиції, а також налаштування навігаційного меню. Об’єкт AstroCalculator 
виконує астрономічні обчислення, зокрема визначення юліанської дати, зоряного 
часу та місцевого зоряного часу, а також розрахунок горизонтальних координат 
зірок. StarCatalogActivity управляє каталогом зірок, працюючи з базою даних 
StarDbHelper та адаптером StarAdapter, який забезпечує відображення списку 
зірок, обробку кліків і визначення видимості зірок. Клас Star представляє дані 
зірок із назвою, прямим сходженням, схиленням і спектральним типом, а 
StarDbHelper забезпечує доступ до локальної бази даних для отримання партій 
зірок або пошуку за запитом. TimeSiderealActivity обробляє геопозицію та 
розраховує зоряний час із використанням LocationServices, передаючи дані для 
3D-візуалізації через EarthGLSurfaceView та EarthRenderer, які відповідають за  
42 
ЧДТУ 252477.002 ПЗ 
 
Рисунок 3.3 - Діаграма класів 
 
рендеринг моделі Землі, масштабування, обертання та встановлення маркерів. 
TiltView забезпечує анімаційне відображення нахилу для інтерактивної взаємодії 
43 
ЧДТУ 252477.002 ПЗ 
користувача. Сенсорні активності, включно з AccelerometerActivity, 
GyroscopeActivity, MagnetometerActivity та PolarisFinderActivity, успадковують 
BaseActivity та працюють із SensorManager для отримання та обробки даних 
відповідних сенсорів. PolarisFinderActivity додатково виконує розрахунки азимута 
для наведення на Полярну зірку, інтегруючи сенсорні дані з обчисленнями 
AstroCalculator. MainActivity координує загальні функції, зокрема запуск 
таймлапсу, та взаємодіє з усіма сенсорними компонентами. Усі компоненти 
інтегруються для забезпечення управління каталогом зірок, обчислення зоряного 
часу та навігаційних параметрів, обробки сенсорних даних та відображення 
інтерактивної 3D-моделі Землі з маркерами користувача, що забезпечує цілісну 
архітектуру платформи. UML-діаграми класів і компонентів створювались із 
використанням нотації PlantUML [5]. 
3.2.3.2 Діаграми пакетів 
Представлена діаграма пакетів (Рис 3.4) відображає архітектурну структуру 
програмної платформи комплексу оптичної астронавігації. Пакет Core включає 
основні класи програми (BaseActivity та MainActivity) і забезпечує єдиний 
контекст виконання для всіх модулів.  
Пакет Sensors містить сенсорні активності для обробки даних 
акселерометра, гіроскопа, магнітометра та обчислення азимута на Полярну зорю  
через PolarisFinderActivity.  
Пакет AstroTime реалізує астрономічні розрахунки часу та координат через 
TimeSiderealActivity, дані з якого передаються до пакету Visualization для 3D-
відображення Землі та анімації нахилу (EarthGLSurfaceView, EarthRenderer, 
TiltView).  
Пакет StarCatalog управляє каталогом зірок через StarCatalogActivity, 
StarAdapter та клас даних Star, отримуючи інформацію з пакету DB 
(StarDbHelper). Стрілки на діаграмі відображають напрямки залежностей між 
пакетами та взаємодію модулів, забезпечуючи інтегровану роботу сенсорів, 
обчислень, візуалізації та роботи з каталогом зірок.  
44 
ЧДТУ 252477.002 ПЗ 
 
Рисунок 3.4 - Діаграма пакетів платформи POA 
 
Діаграма (Рис 3.5) відображає логічну структуру платформи оптичної 
астронавігації. Пакет Core забезпечує керування всією системою, запускаючи 
сенсорну підсистему (Sensors), модуль астрономічних розрахунків (AstroTime) та 
каталог зірок (Catalog). Пакет Sensors обробляє дані з акселерометра, гіроскопа, 
магнітометра та визначає азимут на Полярну зорю. AstroTime відповідає за 
45 
ЧДТУ 252477.002 ПЗ 
обчислення зоряного часу та координат. Пакет Vis забезпечує 3D-візуалізацію 
Землі та орієнтаційних показників. Catalog працює з базою даних, надаючи 
інформацію про зірки. Взаємодія пакетів забезпечує цілісну роботу системи. 
 
 
Рисунок 3.5  - Діаграма пакетів/Логічна структура програми 
 
3.2.4 Архітектурне проектування 
Архітектурне проектування є ключовим етапом у розробці програмного 
забезпечення, оскільки визначає загальну структуру та організацію системи. Воно 
передбачає розробку концепції системи, вибір архітектурних патернів та 
визначення основних компонентів програмної платформи комплексу оптичної 
астронавігації. 
Метою архітектурного проектування є створення такої архітектури, яка 
забезпечує високу продуктивність, надійність, масштабованість і ефективну 
взаємодію між сенсорними даними, астрономічними обчисленнями, каталогом 
зірок та візуалізацією 3D-моделі Землі. У процесі проектування враховуються 
46 
ЧДТУ 252477.002 ПЗ 
вимоги до системи, особливості доменної області та сучасні технології розробки 
на платформі Android із використанням Kotlin і OpenGL. 
У цьому розділі буде розглянуто ключові аспекти архітектурного 
проектування для програмної платформи, включно з діаграмами компонентів та їх 
взаємозв’язками, що відображають організацію та логіку роботи всіх підсистем. 
3.2.4.1 Діаграма компонентів 
Діаграма компонентів (Рис 3.6) відображає основні програмні модулі 
системи та взаємодію між ними. Система складається з чотирьох ключових 
підсистем: сенсорної обробки, астрономічних обчислень, 3D-візуалізації та 
модуля каталогу зірок. Кожен компонент виконує окрему логічну функцію, а 
зв’язки між ними демонструють передачу даних і залежності. 
 
 
Рисунок 3.6 - Діаграма компонентів модулів системи 
47 
ЧДТУ 252477.002 ПЗ 
3.2.4.2 Діаграма розгортання  
Діаграма розгортання (Рис 3.7) відображає структуру системи та спосіб 
взаємодії її складових на реальному пристрої. Вона демонструє, як програмні 
модулі працюють у середовищі Android, яким апаратним сенсорам вони 
відповідають і які зовнішні служби залучаються. Графічна модель наочно показує 
потоки даних між компонентами: датчиками пристрою, обчислювальними 
модулями, 3D-візуалізатором, локальною базою даних та сервісами геолокації.  
 
 
Рисунок 3.7 - Діаграма розгортання 
48 
ЧДТУ 252477.002 ПЗ 
3.2.5 Моделювання поведінки системи 
Моделювання поведінки системи є ключовим етапом під час розробки 
комплексу оптичної астронавігації, оскільки дозволяє визначити, як програмна 
платформа реагує на дії користувача, зміну орієнтації пристрою, надходження 
сенсорних даних та зовнішні умови. На цьому етапі описуються типові сценарії 
роботи застосунку - від отримання сигналів акселерометра, гіроскопа та 
магнітометра до обчислення зоряного часу, визначення напрямку на Полярну 
зорю та візуалізації моделі Землі. 
Поведінкове моделювання дає змогу проаналізувати логіку взаємодії між 
внутрішніми модулями: сенсорною підсистемою, блоком астрономічних 
обчислень, 3D-візуалізатором та каталогом зірок. Для формалізації цих процесів 
використовуються діаграми діяльності, послідовності, комунікації та скінченного 
автомата, що відображають реакцію системи на зміну орієнтації пристрою, запити 
на оновлення місцезнаходження або звернення до бази даних. 
Результатом моделювання є чітке представлення алгоритмів роботи 
комплексу в різних ситуаціях: визначення азимуту за даними сенсорів, 
обчислення місцевого зоряного часу, оновлення 3D-моделі та відображення 
позиції користувача у просторі. Це забезпечує розуміння функціональності 
системи, її поведінкових закономірностей і дозволяє уникнути помилок під час 
реалізації та подальшого тестування програмного забезпечення. 
3.2.5.1 Діаграма діяльності 
Діаграма діяльності (Рис 3.8) демонструє логіку роботи модуля камери в 
мобільному додатку астронавігації (MainActivity). Вона починається з ініціалізації 
активності, перевірки дозволів на доступ до камери та сховища, після чого 
відбувається ініціалізація CameraManager та сенсорів пристрою. Далі камера 
відкривається, запускається прев’ю, і користувач може взаємодіяти з інтерфейсом 
через кнопки налаштувань або старт тайм-лапсу. При виконанні тайм-лапсу 
відбувається циклічне створення знімків, запис метаданих сенсорів, збереження 
файлів на фоні та інкремент лічильника зроблених фото до досягнення 
49 
ЧДТУ 252477.002 ПЗ 
максимального числа. Паралельно здійснюється постійне оновлення даних 
сенсорів, таких як азимут та кути нахилу, для забезпечення коректного знімання 
та точності навігаційних параметрів. Діаграма демонструє розділення потоків між 
UI-потоком, де відбувається взаємодія з користувачем, та фоновим потоком, де 
здійснюються операції збереження та обробки даних, дозволяючи наочно 
побачити циклічні та умовні процеси, взаємозв’язок між модулями та порядок 
виконання дій у модулі камери 
 
 
Рисунок 3.8 Діаграма діяльності 
50 
ЧДТУ 252477.002 ПЗ 
3.2.5.2 Діаграма послідовності  
Діаграма послідовності (Рис 3.9) коротко показує, як різні модулі додатку 
взаємодіють між собою: отримання координат і обчислення зоряного часу у 
TimeSiderealActivity, обробка даних сенсорів у PolarisFinderActivity з оновленням 
орієнтації, та пошук і відображення зір у StarCatalogActivity через базу даних.  
 
 
Рисунок 3.9 - Діаграма послідовності 
51 
ЧДТУ 252477.002 ПЗ 
Вона відображає циклічні процеси, умовні перевірки та оновлення UI у 
реальному часі.  
3.2.5.3 Діаграма комунікації 
Діаграма комунікації (Рис 3.10) показує ключові кроки взаємодії 
користувача з додатком та обробку даних у фонових процесах. Вона ілюструє, як 
TimeSiderealActivity отримує координати, обчислює зоряний час і оновлює 3D-
візуалізацію, а також як StarCatalogActivity обробляє пошукові запити через 
корутини та базу даних, забезпечуючи оновлення інтерфейсу користувача у 
реальному часі. 
 
 
Рисунок 3.10 - Діаграма комунікації 
52 
ЧДТУ 252477.002 ПЗ 
3.2.5.4 Діаграма скінченного автомату 
Діаграма скінченного автомату (Рис 3.11) відображає логіку роботи 
PolarisFinderActivity. Вона показує два основні стани: «Пошук» - коли полярна 
зірка ще не визначена, і «Знайдено» - коли азимут близький до півночі. Перехід 
між станами відбувається на основі змін азимуту, а вихід із станів здійснюється 
при призупиненні активності. Це дозволяє наочно зрозуміти, як система реагує на 
зміни орієнтації пристрою в реальному часі. 
 
 
Рисунок 3.11 - Діаграма скінченного автомату  
 
  
53 
ЧДТУ 252477.002 ПЗ 
ВИСНОВОК ДО РОЗДІЛУ 3 
Розділ охоплює ключові етапи розробки програмної платформи комплексу 
оптичної астронавігації для мобільних пристроїв. Він включає формування вимог, 
проектування логічної структури та архітектури системи, а також моделювання її 
поведінки. Використання різних типів діаграм, таких як діаграми класів, пакетів, 
компонентів, діяльності, послідовності, комунікації та скінченного автомату, 
дозволяє систематично описати внутрішні процеси та взаємодію між 
підсистемами.  
Цей підхід забезпечує ефективну інтеграцію сенсорної підсистеми, модуля 
астрономічних обчислень, локальної бази даних зірок та підсистеми інтерактивної 
3D-візуалізації Землі, що дозволяє користувачу отримувати точні навігаційні дані, 
відстежувати місцевий зоряний час та визначати напрямок на Полярну Зорю. 
Результатом розробки є високоякісна та функціональна платформа, яка 
забезпечує зручний інтерфейс, інтерактивну взаємодію з небесними об’єктами та 
наочне відображення інформації у реальному часі, задовольняючи потреби 
користувачів у астрономічних спостереженнях, навчанні та наукових 
дослідженнях. 
 
54 
ЧДТУ 252477.002 ПЗ 
РОЗДІЛ 4 РОЗРОБКА ТА ТЕСТУВАННЯ ПРОГРАМНОГО 
ЗАБЕЗПЕЧЕННЯ 
На цьому етапі відбувається реалізація вимог, сформованих під час аналізу 
та проектування платформи оптичної астронавігації для мобільних пристроїв. 
Потрібно забезпечити інтеграцію сенсорної підсистеми, модулів астрономічних 
обчислень, локальної бази даних зірок та інтерактивної 3D-візуалізації Землі.  
Одночасно проводиться тестування функціональності та надійності системи 
для перевірки відповідності програмного забезпечення встановленим критеріям 
якості. У розділі описано основні завдання, застосовані стратегії реалізації та 
методи тестування, що дозволяють гарантувати точність астрономічних 
розрахунків, коректність обробки сенсорних даних та стабільну роботу 
користувацького інтерфейсу. 
4.1 Розробка програмного комплексу 
Розробка програмного комплексу платформи оптичної астронавігації 
передбачає послідовне виконання етапів, спрямованих на створення 
функціональної, стабільної та зручної для користувача системи. Основні кроки 
включають аналіз вимог, проектування архітектури платформи, написання коду 
для сенсорної підсистеми, модулів астрономічних обчислень, локального каталогу 
зірок та інтерактивної 3D-візуалізації Землі, а також тестування та впровадження. 
Аналіз вимог зосереджений на виявленні потреб користувачів щодо 
точності астрономічних розрахунків, інтерактивності 3D-візуалізації та зручності 
роботи з даними сенсорів і GPS. Тестування включає функціональні та 
нефункціональні перевірки, що гарантують правильність обчислень, стабільність 
роботи додатку та відповідність встановленим критеріям якості. 
У цьому розділі детально розглядаються кожен з етапів розробки, 
використані методології та інструменти, а також представлені результати 
реалізації та стратегії тестування програмного комплексу. 
4.1.1 Обґрунтування вибору засобів реалізації 
55 
ЧДТУ 252477.002 ПЗ 
Для реалізації задач платформи оптичної астронавігації було використано 
такі засоби: 
1 Kotlin та Android SDK: Основною мовою розробки обрано Kotlin для 
реалізації мобільного додатку на платформі Android. Kotlin забезпечує 
безпечну роботу з типами, компактний та читабельний код, а також 
зручну інтеграцію з Android SDK для доступу до сенсорів та геопозиції. 
Під час розробки окремих програмних компонентів та аналізу 
синтаксичних конструкцій Kotlin використовувалися інструменти 
автоматизованого пошуку та аналізу інформації [1]. 
2 OpenGL ES: Для реалізації інтерактивної 3D-візуалізації Землі та 
навігаційних елементів використовується OpenGL ES 2.0. Це дозволяє 
створювати реалістичну графіку, анімацію нахилу та обертання планети, 
а також відображення маркера місцезнаходження користувача. 
3 Сенсорні API Android: Використання SensorManager та інтерфейсу 
SensorEventListener дозволяє зчитувати дані з акселерометра, гіроскопа 
та магнітометра для визначення орієнтації пристрою, обчислення кутів 
нахилу та азимута на Полярну зірку. 
4 Google Location Services: Забезпечує отримання точних географічних 
координат користувача (широта, довгота), необхідних для розрахунку 
місцевого зоряного часу та точного визначення орієнтації. 
5 SQLite: Локальна база даних використовується для зберігання каталогу 
небесних об’єктів, що дозволяє швидко здійснювати пошук та 
відображення зірок у додатку. 
6 Python: Інтеграція Python у додаток для післяобробки даних, отриманих 
у процесі серійної фотозйомки. Python-скрипти застосовуються для 
аналізу RAW-зображень (DNG) та супровідних JSON-метаданих, 
виконання додаткових астрономічних і статистичних обчислень, а також 
для обробки та узагальнення результатів вимірювань. 
Завдяки такому поєднанню технологій забезпечується точність обчислень, 
висока продуктивність та інтерактивність мобільного додатку, а також 
56 
ЧДТУ 252477.002 ПЗ 
можливість масштабування і подальшого вдосконалення платформи для 
навчальних і наукових цілей. 
Реалізація роботи з сенсорами та модулем камери виконувалась із 
використанням стандартних API платформи Android відповідно до офіційної 
документації [3]. 
4.1.2 Опис структурної (функціональної) схеми 
Структурна схема відображена на рисунку 4.1 включає наступні 
компоненти. 
1 Клієнтська частина (Frontend / Мобільний додаток): 
 Тематичні сторінки: відображення поточного зоряного часу, 
координат користувача та основних навігаційних показників (нахил, 
азимут, орієнтація). 
 Каталог зірок: перегляд списку небесних об’єктів із локальної бази 
даних, можливість пошуку за назвою, спектральним типом або 
координатами. 
 Сторінка детальної інформації про зірку: відображення параметрів 
обраного небесного об’єкта (пряме сходження, схилення, 
спектральний тип). 
 3D-візуалізація Землі: інтерактивна модель з маркером 
місцезнаходження користувача, анімація нахилу та обертання 
планети, відображення напрямку на Полярну зірку. 
 Налаштування та персоналізація: можливість змінювати 
інтерфейс, обирати додаткові параметри відображення. 
2 Серверна частина / Логіка обробки даних: 
 Обробка сенсорних даних: прийом значень акселерометра, 
гіроскопа та магнітометра, перетворення їх у навігаційні параметри 
(кути нахилу, азимут). 
 Геопозиційні обчислення: отримання координат користувача через 
GPS та розрахунок місцевого зоряного часу (LST). 
57 
ЧДТУ 252477.002 ПЗ 
 Астрономічні розрахунки: обчислення Юліанської дати, GMST, 
LST та азимута на Полярну зірку. 
 Обробка запитів каталогу зірок: пошук, фільтрація та підготовка 
даних для відображення у списках та деталях об’єктів. 
 
 
Рисунок 4.1 – Структурна схема системи 
58 
ЧДТУ 252477.002 ПЗ 
 
Рисунок 4.2 – Функціональна схема системи 
 
3 База даних (SQLite): 
 Таблиця “Stars”: зберігання інформації про небесні об’єкти (назва, 
пряме сходження, схилення, спектральний тип). 
 Таблиця “UserSettings”: зберігання персональних налаштувань 
користувача та параметрів інтерфейсу. 
4.1.3 Опис логічної схеми системи 
Логічна схема системи(4.3) описує взаємодію між користувачем та різними 
модулями мобільного додатку: 
Клієнтська частина (Frontend / Мобільний додаток): 
1 Користувач переходить на головний екран, де відображаються елементи 
керування камерою, попередній перегляд зображення, індикатор 
прогресу зйомки та інформаційні повідомлення про стан збору даних. 
2 Обираючи конкретний небесний об’єкт, користувач переходить на 
сторінку детальної інформації про зірку, де відображаються її 
координати, спектральний тип та інші параметри. 
3 В відповідному модулі можна зробити пошук полярної зорі. 
4 Для зміни мови інтерфейсу або персоналізації користувач використовує 
відповідні налаштування. 
5 Також можна використовувати каталог зірок для пошуку та фільтрації 
небесних об’єктів. 
59 
ЧДТУ 252477.002 ПЗ 
6 Через модулі 3D-візуалізації користувач спостерігає модель Землі з 
маркером свого розташування та напрямком на Полярну зірку. 
7 Локальна логічна частина (обробка даних на пристрої): 
 
 
Рисунок 4.3- Опис логічної схеми системи 
 
8 Обробка сенсорних даних: зчитування показників акселерометра, 
гіроскопа та магнітометра з подальшим зберіганням значень для 
кожного кадру фотозйомки. 
9 Геопозиційні обчислення: отримання GPS-координат користувача та 
обчислення місцевого зоряного часу (LST). 
10 Астрономічні обчислення: розрахунок Юліанської дати, GMST, LST та 
азимута на Полярну зірку. 
11 Обробка запитів каталогу зірок: пошук, фільтрація та передача даних у 
Frontend. 
60 
ЧДТУ 252477.002 ПЗ 
12 Збереження та отримання даних із бази SQLite: таблиці “Stars” та 
“UserSettings” для каталогів і персональних налаштувань користувача. 
4.1.4 Розробка бази даних 
У поточному мобільному додатку для астронавігації зірок реалізовано 
локальну систему зберігання та обробки даних, яка забезпечує швидкий доступ до 
інформації та автономну роботу користувача. Для зберігання каталогу зірок 
використано вбудовану SQLite [15]. 
Зберігання та каталог зірок 
 Для ефективного пошуку та збереження даних про зірки використовується 
локальна база даних SQLite (StarDbHelper). Користувач може швидко знаходити 
потрібні записи та переглядати каталог без підключення до Інтернету. Початкові 
дані каталогу можуть бути завантажені з локальних JSON-файлів (assets), що 
спрощує початкове заповнення бази та її оновлення. Всі операції з базою даних 
виконуються асинхронно за допомогою механізмів lifecycleScope.launch 
(Dispatchers.IO) та withContext(Dispatchers.Main), щоб уникнути блокування 
інтерфейсу користувача. 
Управління налаштуваннями користувача 
 Прості налаштування, такі як вибір мови інтерфейсу, зберігаються у 
SharedPreferences. Це дозволяє зберігати невеликі параметри у форматі ключ-
значення та швидко відновлювати їх при повторному запуску додатку. 
Взаємодія модулів та даних 
 Локальна база даних тісно інтегрована з основними модулями додатку. 
Каталог зірок взаємодіє з модулем астрономічних обчислень (StarCatalogActivity, 
Star.kt, StarAdapter.kt) для пошуку та відображення даних. Сенсорні модулі 
(AccelerometerActivity, MagnetometerActivity, GyroscopeActivity, 
PolarisFinderActivity) використовують отримані дані для визначення положення та 
напрямку. Результати обчислень і обробки даних передаються до візуалізаційного 
модуля (EarthGLSurfaceView, TiltView), що дозволяє користувачу бачити 
актуальні координати та орієнтацію 
 
61 
ЧДТУ 252477.002 ПЗ 
 
Рисунок 4.4 - Вигляд таблиці з БД 
 
4.1.5 Розробка інтерфейсу користувача 
Програмне забезпечення, платформа комплексу оптичної астронавігації, має 
7 сторінок:  
1 Головна сторінка 
2 Каталог зірок 
3 Зоряний час 
4 Пошук полярної зірки 
5 Акселерометр 
6 Магнітометр 
7 Гіроскоп  
Перша сторінка (Рис 4.5) є основною робочою областю додатку, у якій  
користувач керує процесом фотозйомки та збору експериментальних даних. 
62 
ЧДТУ 252477.002 ПЗ 
 
Рисунок 4.5 - Інтерфейс головної сторінки 
 
 
Рисунок 4.6 - Інтерфейс вікна налаштувань камери 
63 
ЧДТУ 252477.002 ПЗ 
 
Рисунок 4.7 - Інтерфейс головної сторінки після виконання фото 
 
На другій сторінці користувач побачить список зоряних об'єктів (Рис 4.8) і 
має можливість зробити пошук (Рис 4.10) та подивитись інформацію про них (Рис 
4.9). 
 
 
Рисунок 4.8 - Інтерфейс сторінки каталогу зір 
64 
ЧДТУ 252477.002 ПЗ 
 
Рисунок 4.9 - Вигляд інформації про зорю 
 
 
Рисунок 4.10 - Інтерфейс сторінки, коли відбувся пошук 
 
На третій відображені значення прискорення, що змінюються від руху 
телефону і відображають орієнтацію/стан телефону (Рис 4.11), також 
65 
ЧДТУ 252477.002 ПЗ 
відображається ідентифікатор точності відповідним кольором(висока/зелений, 
середня/жовтий, низька/червоний, ненадійна/сірий). 
 
 
Рисунок 4.11 - Інтерфейс сторінки акселерометру 
 
Четверта сторінка – гіроскоп (Рис 4.12). Відображає кути і швидкості 
обертання осей. Також присутній опис руху та інтенсивності, для кращого 
розуміння стану телефону.  
 
 
Рисунок 4.13 - Інтерфейс сторінки гіроскопу 
66 
ЧДТУ 252477.002 ПЗ 
П'ята сторінка є магнітометром (Рис 4.14), вона відображає 
напруженість магнітного поля. Обертаючи телефон користувач може 
спостерігати за зміною магнітного поля та визначенням напрямку. 
 
 
Рисунок 4.13 - Інтерфейс сторінки магнітометру 
 
Шоста сторінка - пошук полярної зорі. Користувач може спостерігати 
за стрілкою, яка вказує на поточний напрямок, для пошуку зорі (Рис 4.13), а 
коли сторону обрано вірно, то буде анімація (Рис 4.14). 
 
  
Рисунок 4.14 - Інтерфейс сторінки пошуку полярної зірки 
67 
ЧДТУ 252477.002 ПЗ 
 
Рисунок 4.15 - Інтерфейс сторінки , коли шлях вірний 
 
Сьома сторінка - зоряний час(4.17). Користувач побачить(4.18) свій 
локальний час( прогрес бар, що відображає час, що минув з початку доби), UTC, 
Зоряний Час (LST), Юліанська дата (JD), Загальний Зоряний Час (GMST). Є 
можливість натиснути кнопку, щоб отримати поточні GPS-координати та їх 
відображення на 3D-моделі Землі(4.16). 
 
 
Рисунок 4.16 - Інтерфейс з відображення місцезнаходження 
68 
ЧДТУ 252477.002 ПЗ 
 
Рисунок 4.17 - Інтерфейс сторінки зоряний час 
 
 
Рисунок 4.18 - Частина інтерфейсу часу 
 
4.1.6 Опис розробки програмних компонентів 
69 
ЧДТУ 252477.002 ПЗ 
Розробка програмних компонентів додатку POA передбачає створення 
модульних частин, які забезпечують взаємодію з сенсорами пристрою, 
обчислення астрономічних даних та відображення каталогу зірок. Компоненти 
поділяються на кілька основних груп. 
Компоненти навігації та основи (Framework) 
1 Базова Активність (BaseActivity) 
Надає єдину структуру для всіх екранів додатку. Включає Toolbar, 
логіку для Navigation Drawer (бічного меню) та механізм зміни мови 
інтерфейсу (UA/EN). 
2 Навігаційний Дровер (Drawer Navigation) 
Відображає список усіх функціональних модулів додатку, включно зі 
зміною мови та посиланням на документацію або зовнішні ресурси. 
3 Головний Екран (MainActivity) 
Стартовий екран, є центральним модулем додатку, що відповідає за 
ініціацію серійної фотозйомки, реєстрацію сенсорних і геопозиційних даних 
та формування набору експериментальних даних для подальшого аналізу. 
Компоненти каталогу та роботи з даними 
1 Каталог Зірок (StarCatalogActivity) 
Основний контролер для відображення даних каталогу зірок. 
Відповідає за ініціалізацію компонентів даних та обробку життєвого 
циклу сторінки. 
2 Елементи каталогу (StarAdapter) 
Адаптер для RecyclerView, що візуалізує окремі записи зірок, 
відображає назву та дозволяє обробляти натискання на елемент. 
3 Пошук (SearchBox) 
 Дозволяє користувачу вводити запит для пошуку зірок. Використовує 
TextWatcher та асинхронний пошук у локальній базі SQLite. 
4 Завантаження та пагінація 
Забезпечує завантаження даних порціями (offset, batchSize) та 
асинхронну взаємодію з базою для уникнення блокування інтерфейсу. 
70 
ЧДТУ 252477.002 ПЗ 
Компоненти сенсорів та візуалізації 
1 Стрілка Полярної (PolarisFinder Arrow) 
Відображає напрямок на Полярну зірку. Обертання стрілки залежить 
від обчисленого азимуту. 
2 Компонент світіння (Glow Effect) 
Анімація змінює колір та прозорість стрілки, коли азимут знаходиться 
в допустимому діапазоні (менше 10°). 
3 3D-візуалізація Землі 
Рендеринг 3D-моделі Землі з підтримкою жестів (масштабування, 
обертання) та встановлення маркеру локації. 
4 Відображення сенсорів 
Екрани для відображення сирих даних сенсорів (X, Y, Z) та 
обчислених значень: нахилу (Roll/Pitch), інтенсивності обертання, 
напрямку. 
Компоненти обчислень 
1 Обчислення часу та зоряного часу (LST) 
Виконує розрахунок Юліанської дати, GMST та LST. Отримує GPS-
координати користувача для точних астрономічних обчислень. 
2 Цикл оновлення часу 
Асинхронний цикл оновлення значень кожну секунду 
(lifecycleScope.launch/delay(1000)), що забезпечує відображення 
реального часу LST та GMST. 
3 Індикатор дня 
ProgressBar для візуалізації прогресу дня, що минув, допомагає 
користувачу орієнтуватися у часі відносно місцевої доби. 
Отримання географічних координат здійснювалося за допомогою 
сервісів геолокації[9]. 
4.2 Тестування системи 
У цьому розділі представлено стратегію тестування додатку POA, а також  
71 
ЧДТУ 252477.002 ПЗ 
проведено валідацію та верифікацію розробленого програмного забезпечення. 
Мета цього етапу - перевірити коректність роботи всіх модулів, точність 
астрономічних обчислень, правильність взаємодії з сенсорами пристрою та 
стабільність додатку в цілому перед його використанням кінцевими 
користувачами. 
Ретельне тестування дозволяє своєчасно виявити та усунути помилки, що 
можуть виникнути під час роботи з сенсорами, відображення каталогу зірок, 3D-
візуалізації. Також проведено тестування стабільності серійної RAW-фотозйомки, 
коректності формування DNG-файлів та відповідності JSON-метаданих 
фактичним сенсорним і геопозиційним даним. 
У наступних підрозділах буде детально розглянуто планування тестів, 
методи їх виконання та аналіз отриманих результатів для забезпечення високої 
надійності та функціональності додатку. 
4.2.1 Модульне тестування 
Перевіримо: 
1 астрономічних обчислення, правильності перетворення стандартних дат 
та високосних років у відповідну Юліанську дату. 
2 розрахунку загального зоряного часу на основі відомих значень 
Юліанської дати, отриманих з астрономічних довідників. 
3 чи правильно LST обчислюється, використовуючи GMST та задану 
довготу. 
4 функцію, яка нормалізує кут, забезпечуючи, що результат завжди 
лежить у діапазоні (0-360). 
5 логіки, яка визначає, чи повинен активуватися ефект світіння , на основі 
різних значень різниці кутів. 
6 інтенсивності та обертання і вивід вірної інформації 
В результаті тестів всі 6 пунктів реалізовані коректно і пройшли 
перевірку. Для прикладу, при відкритті сторінки в емуляторі(Pixel 9) дані 
будуть такими (Рис 4.19). Отже це один з доказів, що логіка працює 
успішно. 
72 
ЧДТУ 252477.002 ПЗ 
 
Рисунок 4.19 - Перевірка роботи в емуляторі 
 
4.2.2 Інтеграційне тестування 
Перевіримо роботу бекенду з базою даних. Для цього відкриємо другу 
сторінку (Рис 4.20) та спробуємо зробити пошук (Рис 4.21). 
 
 
Рисунок 4.20 - Приклад виводу зорей з БД 
73 
ЧДТУ 252477.002 ПЗ 
 
Рисунок 4.21 - Приклад результатів пошуку 
 
4.2.3 Системне тестування 
Системне тестування є важливим етапом життєвого циклу розробки 
програмного забезпечення, під час якого перевіряється повна інтегрована система 
для оцінки її відповідності визначеним вимогам. На цьому етапі тестуються всі 
компоненти системи разом(Табл 3.1), щоб переконатися, що всі частини 
працюють належним чином і взаємодіють коректно. 
Системне тестування проведено успішно, всі вимоги виконано. 
4.2.4 Приймальне тестування 
Приймальне тестування є завершальним етапом перевірки програмного 
забезпечення, під час якого встановлюється, чи відповідає розроблений 
застосунок POA функціональним вимогам, технічним критеріям та очікуванням 
користувачів. 
74 
ЧДТУ 252477.002 ПЗ 
У межах проекту було створено повний дизайн інтерфейсу застосунку (Рис. 
4.5 - 4.21), який вирізняється продуманою структурою, зручністю навігації та 
візуальною узгодженістю між модулями - від роботи з сенсорами до перегляду 
каталогу зірок та астрономічних обчислень. Інтерфейс забезпечує інтуїтивний 
користувацький досвід, а також створює комфортні умови для взаємодії з 3D-
візуалізацією, сенсорними даними та довідковою інформацією. 
Крім того, під час розробки застосовані сучасні підходи та алгоритми 
обробки даних: точні астрономічні обчислення (LST, GMST, JD), нормалізація 
даних сенсорів, асинхронна взаємодія з локальною базою даних SQLite та 3D-
рендеринг Землі через OpenGL ES. Це забезпечує унікальні функціональні 
можливості застосунку та підвищує його надійність і практичну цінність. 
Таким чином, приймальне тестування підтверджує, що система відповідає 
основним вимогам, демонструє стабільність роботи та готова до подальшого 
використання. 
Загальна оцінка:  
Розроблене програмне забезпечення POA демонструє стабільну роботу та 
повністю виконує поставлені функціональні завдання. Усі основні модулі - від 
сенсорних обчислень до каталогу зірок і 3D-візуалізації - працюють коректно та 
забезпечують корисний і зручний користувацький досвід. 
Надалі застосунок може бути розширений новими можливостями та 
інструментами, що дозволить підвищити його ефективність і збільшити 
функціональність для користувачів. 
 
Таблиця 4.1  
Результати модульного, ітераційного, системного, приймального 
тестування 
Функція Опис дії (Тест / Функціональне 
Протестовано Працює 
(Компонент) Призначення) 
 
 
75 
ЧДТУ 252477.002 ПЗ 
Продовження таблиці 4.1 
Перевірка повного потоку: 
введення тексту виклик 
Пошук у каталозі 
Інтеграційне Так асинхронного запиту до SQLite і 
Зірок 
відображення відфільтрованих 
результатів у RecyclerView. 
Перевірка, що жести 
масштабування та обертання на 
Обробка 3D 
Інтеграційне Коректно 3D-моделі  коректно викликають 
Жестів 
методи зміни дистанції та кутів 
рендерера. 
Розрахунок Модульне Так Тестування розрахунку кутів 
Roll/Pitch (Unit) крену (Roll) та тангажу (Pitch) на 
основі фіксованих вхідних 
векторів прискорення 
Класифікація Модульне Коректно Перевірка логіки when у 
Руху (Unit) GyroscopeActivity.kt, яка 
класифікує та описує рух 
пристрою (наприклад, "Rotation: 
High Intensity") на основі 
порогових значень кутової 
швидкості 
Зміна Мови Інтеграційне Так Наскрізна перевірка: Зміна мови 
через Navigation Drawer 
збереження у SharedPreferences 
коректне застосування нової 
локалі в усіх Activity 
 
 
 
76 
ЧДТУ 252477.002 ПЗ 
Продовження таблиці 4.1 
Серійна Інтеграційне Так Перевірка коректної роботи 
фотозйомка та серійної RAW-фотозйомки через 
збір даних Camera2 API, збереження DNG-
зображень та формування 
відповідних JSON-файлів із 
сенсорними даними (акселерометр, 
гіроскоп, магнітометр), GPS-
координатами та обчисленим 
місцевим зоряним часом (LST). 
Обчислення Модульне Так Перевірка правильності 
Юліанської Дати (Unit) перетворення довільних дат та 
(JD) врахування високосних років у 
стандартну Юліанську дату 
Обчислення Модульне Коректно Перевірка розрахунку Загального 
GMST/LST (Unit) Зоряного Часу (GMST) та 
Місцевого Зоряного Часу (LST) на 
основі JD та заданої довготи згідно 
з астрономічними формулами 
Цикл Оновлення Інтеграційне Так Перевірка, що асинхронна функція 
Часу (lifecycleScope.launch) коректно 
оновлює та виводить обчислені 
значення LST, GMST та JD на 
екран кожну секунду 
Інтеграція GPS та Інтеграційне Так Перевірка, що отримані GPS-
3D координати (широта/довгота) 
використовуються для розрахунку 
LST та встановлення маркера на 
3D-моделі Землі 
 
 
77 
ЧДТУ 252477.002 ПЗ 
Продовження таблиці 4.1 
Обчислення Модульне Так Тестування логіки нормалізації 
Азимуту (Unit) кута, забезпечуючи, що результат 
завжди лежить у діапазоні (0-360) 
Ефект Світіння Модульне Коректно Перевірка активації/деактивації 
Полярної (Unit) анімації світіння та зміни кольору 
тексту, коли обчислений азимут 
потрапляє в діапазон допуску 
о
(менше 10 ) від Півночі 
Інтеграція Інтеграційне Так Імітація даних сенсорів: перевірка, 
Компас/Стрілка що обчислений азимут 
UI відображається у angleText та 
коректно повертає візуальний 
елемент стрілки 
 
4.3 Приклади впровадженого програмного комплексу 
Оскільки основною метою застосунку POA є надання користувачам зручних 
інструментів для роботи з сенсорами, перегляду астрономічних даних, визначення 
положення Полярної зорі, аналізу часу та роботи з каталогом зірок, розробка 
програмного комплексу продовжуватиметься і надалі. 
Подальша робота передбачає розширення функціональності, оновлення 
інтерфейсу та впровадження нових програмних компонентів, що дозволить 
зробити застосунок ще кориснішим та інформативним. Зокрема планується: 
1 удосконалити 3D-візуалізацію Землі; 
2 розширити каталог зірок і реалізувати додаткові фільтри пошуку; 
3 покращити роботу сенсорних модулів для підвищення точності 
вимірювань; 
4 додати нові навчальні або довідкові модулі для користувачів(квести). 
78 
ЧДТУ 252477.002 ПЗ 
Оскільки застосунок працює повністю локально та не використовує 
серверної інфраструктури, його розвиток не вимагає окремого хостингу. Проте 
майбутні оновлення можуть передбачати синхронізацію даних або автоматичне 
отримання великих наборів астрономічної інформації, що потребуватиме 
розгляду можливості інтеграції з віддаленими сервісами або API. Це відкриє шлях 
для подальшого зростання проєкту та підвищення його практичної цінності. 
  
79 
ЧДТУ 252477.002 ПЗ 
ВИСНОВОК ДО РОЗДІЛУ 4 
У розділі 4 було детально розглянуто процес розробки та тестування 
програмного забезпечення платформи оптичної астронавігації для мобільних 
пристроїв. Реалізація системи включала створення функціональних модулів для 
роботи з датчиками, виконання астрономічних обчислень, інтеграцію локальної 
бази даних зірок та впровадження інтерактивної 3D-візуалізації Землі. 
Обґрунтовано вибір технологій, серед яких Kotlin та Android SDK для 
створення клієнтської частини, OpenGL ES для графічного рендерингу, Sensor 
API для роботи з акселерометром, гіроскопом і магнітометром, Google Location 
Services для отримання геокоординат, SQLite для локального зберігання каталогу 
зірок та для інтеграції Python-обчислень. Така комбінація забезпечила точність 
розрахунків, продуктивність та гнучкість розширення можливостей системи. 
У структурних та логічних схемах було описано взаємодію між клієнтською 
частиною, сенсорними модулями, астрономічним ядром та базою даних. 
Реалізовано модулі для відображення зоряного часу, пошуку Полярної зірки, 
роботи з каталогу небесних об’єктів, а також три окремі підсистеми для аналізу 
даних акселерометра, гіроскопа та магнітометра. 
Проведене тестування охоплювало модульні, інтеграційні, системні та 
приймальні перевірки. Було протестовано коректність роботи сенсорних модулів, 
точність астрономічних обчислень, стабільність 3D-візуалізації та правильність 
взаємодії з локальною базою даних. Отримані результати підтвердили 
функціональність і надійність усіх компонентів програмного комплексу. 
Таким чином, програмне забезпечення платформи оптичної астронавігації 
успішно розроблено, інтегровано та протестовано. Реалізовані модулі працюють 
узгоджено, забезпечують точне визначення навігаційних параметрів та зручний 
інтерфейс для користувача, що підтверджує відповідність системи поставленим 
вимогам і можливість використання її у навчальних, наукових та практичних 
задачах. 
  
80 
ЧДТУ 252477.002 ПЗ 
ВИСНОВКИ 
У кваліфікаційній роботі магістра розв’язано актуальну науково-прикладну 
задачу розробки програмної платформи комплексного моніторингу та оптичної 
астронавігації (POA) для мобільних пристроїв. На основі проведених досліджень 
та розробки отримано наступні результати: 
 удосконалено методологію визначення просторової орієнтації 
смартфона шляхом синтезу даних тривісних інерціальних сенсорів 
(акселерометра, гіроскопа та магнітометра). Розв'язано зворотну задачу 
орієнтації, що дозволило ідентифікувати кути тангажу, крену та азимута 
з урахуванням апаратної похибки та магнітних завад. 
 розроблено та імплементовано автономне обчислювальне ядро 
астронавігації (клас AstroCalculator). На основі системного часу та 
геоданих GPS реалізовано високоточні алгоритми розрахунку 
Юліанських дат (JD), гринвіцького та місцевого зоряного часу (LST), що 
забезпечує незалежність системи від зовнішніх мережевих сервісів. 
 вперше запропоновано та реалізовано метод синхронної 
мультимодальної реєстрації даних у модулі інтелектуальної 
фотозйомки (на базі Camera2 API). Наукова новизна полягає у 
формуванні верифікованих наборів даних, де кожне RAW-зображення 
(DNG) жорстко детерміноване супровідними JSON-метаданими, що 
містять вектори станів сенсорів та астрономічні координати на момент 
експозиції. 
 спроектовано та оптимізовано реляційну модель локального 
каталогу зірок засобами SQLite. Застосування асинхронної обробки 
даних (корутини Kotlin) та методів пакетного завантаження дозволило 
забезпечити латентність запитів до великих масивів даних (понад 
200,000 об'єктів) на рівні, придатному для систем реального часу. 
 створено інтерактивну модель геоцентричної 3D-візуалізації з 
використанням OpenGL ES. Реалізовано математичні моделі проекції 
81 
ЧДТУ 252477.002 ПЗ 
географічних координат на полігональну сітку геоїда, що забезпечує 
наочну демонстрацію нахилу земної осі та положення спостерігача з 
високою частотою оновлення кадрів (60+ FPS). 
 розроблено алгоритм динамічної ідентифікації Полярної зорі, який 
інтегрує обчислені горизонтальні координати об’єкта з інерціальною 
орієнтацією пристрою. Реалізовано систему візуальної валідації (ефект 
«активації сяйва»), що підтверджує збіг розрахункової моделі з 
фізичним напрямком. 
 забезпечено гнучкість та розширюваність архітектури шляхом 
розробки уніфікованого базового компонента BaseActivity. Впроваджено 
механізми динамічної локалізації та можливість інтеграції Python-
інтерпретатора для виконання складного статистичного аналізу 
безпосередньо в середовищі Android. 
Практична цінність роботи полягає у створенні цілісного інструментарію 
для аматорської астрономії та дослідницьких цілей, який поєднує можливості 
зйомки з глибокою аналітикою фізичних параметрів спостереження. Результати 
тестування підтвердили стабільність роботи комплексу в динамічних умовах та 
високу точність узгодження сенсорних і оптичних даних. 
Завдання, визначені на етапі проєктування, виконані в повному обсязі, що 
підтверджує адекватність обраних методів дослідження та програмних рішень. 
  
82 
ЧДТУ 252477.002 ПЗ 
СПИСОК ВИКОРИСТАНИХ ДЖЕРЕЛ 
1 "gemini"[Електронний ресурс] - пошук інформації для коду та 
документації: https://gemini.google.com/ 
2 "chatgpt"[Електронний ресурс] - пошук інформації для коду та 
документації:https://chatgpt.com/ 
3 "Android Developers"[Електронний ресурс] - Документація та робота з 
БД: https://developer.android.com/  
4 "Mermaid "[Електронний ресурс] - Для створення діаграм: 
https://mermaid.live/ 
5 "plantuml"[Електронний ресурс] - Для створення діаграм (UML): 
https://www.plantuml.com/plantuml/uml 
6 “Wiki” [Електронний ресурс] - перегляд статей, для написання своїх 
текстів: https://uk.wikipedia.org/ 
7 Методичні рекомендації для написання роботи магістра:  
8 методичні_рекомендації_по_офіормленню_робіт_магістра_22_р.doc 
9 NASA Astronomical Algorithms[Електронний ресурс]: 
https://ssd.jpl.nasa.gov/ 
10 Google Maps / Geolocation API Docs [Електронний ресурс]: 
https://developers.google.com/maps/documentation 
11 Sensor Fusion Concepts [Електронний ресурс]: 
12 https://www.st.com/resource/en/application_note/cd00209702.pdf 
13 Пошук аналогів [Електронний ресурс]: https://play.google.com/store/apps/ 
14 Meeus J. Astronomical Algorithms. - 2nd ed. - Willmann-Bell, 1998. - 477 p. 
15 Duffett-Smith P., Zwart J. Practical Astronomy with your Calculator or 
Spreadsheet. - 4th ed. - Cambridge University Press, 2011. 
16 Sensor Fusion Concepts. Application Note AN2875. STMicroelectronics 
[Електронний ресурс]: https://www.st.com/resource/en/application_note 
/cd00209702.pdf  
17 SQLite. SQL Database Engine Documentation [Електронний ресурс]: 
https://www.sqlite.org/docs.html 
83 
ЧДТУ 252477.002 ПЗ 
18 Jetpack Camera2 API. Android Camera Control [Електронний ресурс]: 
https://developer.android.com/training/camera2 
 
  
 
84 
ДОДАТОК А 
 
ЗАТВЕРДЖЕНО: 
Зав. кафедрою ПЗАС, професор 
_________________ Голуб С.В. 
„____” ______________ 2025 р. 
 
 
 
 
Програмна платформа комплексу оптичної астронавігація 
Специфікація 
ЧДТУ 252477.002 ПЗ 
Листів 2 
 
 
 
 
Розробник ________________ Веретільник В.О. 
Керівник ________________ Голуб С.В. 
 
 
 
 
 
 
 
2025 
 
ЧДТУ 252477.002 ПЗ      2 
Позначення Найменування Примітка 
482.ЧДТУ 252477 12 01 Лістинг програм  
482.ЧДТУ 252477 90 01 Графічні матеріали  
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
 
86 
 
ДОДАТОК Б 
 
 
 
 
Програмна платформа комплексу оптичної астронавігація 
 
 
 
 
 
 
 
 
 
 
 
 
Лістинг програм 
482.ЧДТУ 252477 12 01 
Листів 25 
 
 
 
Розробник                  ________________     Веретільник В.О. 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2025 
 
 
482.ЧДТУ 252477 12 01 2 
ЗМІСТ ЛІСТИНГІВ ФАЙЛІВ ПРОЕКТУ 
 
StarDbHelper.kt .............................................................................................. 89 
AccelerometerActivity.kt ................................................................................ 89 
AstroCalculator.kt ........................................................................................... 90 
BaseActivity.kt ............................................................................................... 91 
EarthRenderer.kt ............................................................................................. 93 
GyroscopeActivity.kt ...................................................................................... 95 
MagnetometerActivity.kt ................................................................................ 96 
MainActivity.kt ............................................................................................... 96 
PolarisFinderActivity.kt .................................................................................. 99 
Star.kt ........................................................................................................... 100 
StarAdapter.kt ............................................................................................... 100 
StarCatalogActivity.kt................................................................................... 100 
TiltView.kt.................................................................................................... 102 
TimeSiderealActivity.kt ................................................................................ 103 
activity_accelerometer.xml ........................................................................... 105 
activity_gyro.xml .......................................................................................... 105 
activity_magnetometer.xml ........................................................................... 106 
activity_main.xml ......................................................................................... 107 
activity_polaris_finder.xml ........................................................................... 108 
activity_star_catalog.xml .............................................................................. 108 
activity_time_sidereal.xml ............................................................................ 109 
dialog_settings.xml ....................................................................................... 110 
item_star.xml ................................................................................................ 111 
drawer_menu.xml ......................................................................................... 111 
AndroidManifest.xml .................................................................................... 111 
 
88 
482.ЧДТУ 252477 12 01 3 
StarDbHelper.kt 
package com.example.poa.db 
import Star 
import android.content.Context 
import android.database.Cursor 
import android.database.sqlite.SQLiteDatabase 
import android.database.sqlite.SQLiteOpenHelper 
import android.util.Log 
import java.io.File 
import java.io.FileOutputStream 
import java.io.InputStream 
class StarDbHelper(private val context: 
Context):SQLiteOpenHelper(context,DATABASE_NAME,null,DATABASE_VERSION){ 
companion object{private const val DATABASE_NAME="stars.db";private const val 
DATABASE_VERSION=15} 
private val dbPath:String get()=context.getDatabasePath(DATABASE_NAME).path 
fun createDatabaseIfNeeded(){val 
dbFile=File(dbPath);if(!dbFile.exists()){copyDatabaseFromAssets();val 
db=SQLiteDatabase.openDatabase(dbPath,null,SQLiteDatabase.OPEN_READWRITE);db.execS
QL("CREATE INDEX IF NOT EXISTS idx_name ON 
stars(name)");db.close();Log.i("StarDbHelper","Database initialized and index 
created")}} 
fungetStarsBatch(offset:Int,limit:Int):List<Star>{valstars=mutableListOf<Star>();v
al db=SQLiteDatabase.openDatabase(dbPath,null,SQLiteDatabase.OPEN_READONLY);val 
cursor:Cursor=db.rawQuery("SELECT name, ra, dec, type FROM stars ORDER BY name 
LIMIT ? OFFSET 
?",arrayOf(limit.toString(),offset.toString()));cursor.use{while(it.moveToNext()){
stars.add(Star(name=it.getString(0).replace("*",""),ra=it.getDouble(1),dec=it.getD
ouble(2),type=it.getString(3)))}};db.close();return stars} 
fun searchStars(query:String):List<Star>{val stars=mutableListOf<Star>();val 
db=SQLiteDatabase.openDatabase(dbPath,null,SQLiteDatabase.OPEN_READONLY);val 
cursor:Cursor=db.rawQuery("SELECT name, ra, dec, type FROM stars WHERE name LIKE ? 
COLLATE NOCASE ORDER BY 
name",arrayOf("%$query%"));cursor.use{while(it.moveToNext()){stars.add(Star(name=i
t.getString(0),ra=it.getDouble(1),dec=it.getDouble(2),type=it.getString(3)))}};db.
close();return stars} 
private fun copyDatabaseFromAssets(){val 
inputStream:InputStream=context.assets.open(DATABASE_NAME);val 
outputFile=File(dbPath);outputFile.parentFile?.mkdirs();val 
outputStream=FileOutputStream(outputFile);val buffer=ByteArray(4096);var 
length:Int;while(inputStream.read(buffer).also{length=it}>0){outputStream.write(bu
ffer,0,length)};outputStream.flush();outputStream.close();inputStream.close();Log.
i("StarDbHelper","Database copied from assets")} 
override fun onCreate(db:SQLiteDatabase){} 
override fun onUpgrade(db:SQLiteDatabase,oldVersion:Int,newVersion:Int){} 
} 
AccelerometerActivity.kt 
package com.example.poa 
import android.annotation.SuppressLint 
import android.hardware.* 
import android.os.Bundle 
import android.view.View 
import android.widget.TextView 
import kotlin.math.abs 
import kotlin.math.atan2 
class AccelerometerActivity:BaseActivity(),SensorEventListener{ 
private lateinit var sensorManager:SensorManager 
private var acc:Sensor?=null 
private lateinit var textX:TextView 
private lateinit var textY:TextView 
89 
482.ЧДТУ 252477 12 01 4 
private lateinit var textZ:TextView 
private lateinit var textInfo:TextView 
override fun 
onCreate(savedInstanceState:Bundle?){super.onCreate(savedInstanceState);setContent
View(R.layout.activity_accelerometer);textX=findViewById(R.id.textAccX);textY=find
ViewById(R.id.textAccY);textZ=findViewById(R.id.textAccZ);textInfo=findViewById(R.
id.textExtraInfo);sensorManager=getSystemService(SENSOR_SERVICE) as 
SensorManager;acc=sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER)} 
override fun 
onResume(){super.onResume();acc?.let{sensorManager.registerListener(this,it,Sensor
Manager.SENSOR_DELAY_GAME)}?:run{textInfo.text=getString(R.string.accelerometer_no
t_found)}} 
override fun onPause(){super.onPause();sensorManager.unregisterListener(this)} 
@SuppressLint("SetTextI18n") 
override fun 
onSensorChanged(event:SensorEvent){if(event.sensor.type!=Sensor.TYPE_ACCELEROMETER
)return;val x=event.values[0];val y=event.values[1];val z=event.values[2];val 
roll=Math.toDegrees(atan2(-x.toDouble(),z.toDouble())).toFloat();val 
pitch=Math.toDegrees(atan2(y.toDouble(),z.toDouble())).toFloat();val 
threshold=15f;val 
orientationText=if(abs(roll)<threshold&&abs(pitch)<threshold){getString(R.string.t
ilt_flat)}else 
if(abs(pitch)>abs(roll)){if(pitch>0)getString(R.string.tilt_back)else 
getString(R.string.tilt_forward)}else{if(roll>0)getString(R.string.tilt_right)else 
getString(R.string.tilt_left)};textX.text="X: %.2f m/s²".format(x);textY.text="Y: 
%.2f m/s²".format(y);textZ.text="Z: %.2f 
m/s²".format(z);textInfo.text="${getString(R.string.accel_info_title)}\n${getStrin
g(R.string.accel_roll_info).format(roll)}\n${getString(R.string.accel_pitch_info).
format(pitch)}\n${getString(R.string.accel_orientation_info).format(orientationTex
t)}"} 
override fun 
onAccuracyChanged(sensor:Sensor?,accuracy:Int){if(sensor?.type!=Sensor.TYPE_ACCELE
ROMETER)return;val indicator=findViewById<View>(R.id.stateIndicator);val 
textState=findViewById<TextView>(R.id.textPhoneState);val(statusText,color)=when(a
ccuracy){SensorManager.SENSOR_STATUS_ACCURACY_HIGH-
>getString(R.string.state_label).format(getString(R.string.intensity_high)) to 
0xFF4CAF50.toInt();SensorManager.SENSOR_STATUS_ACCURACY_MEDIUM-
>getString(R.string.state_label).format(getString(R.string.intensity_medium)) to 
0xFFFFEB3B.toInt();SensorManager.SENSOR_STATUS_ACCURACY_LOW-
>getString(R.string.state_label).format(getString(R.string.intensity_low)) to 
0xFFF44336.toInt();SensorManager.SENSOR_STATUS_UNRELIABLE-
>getString(R.string.state_label).format(getString(R.string.intensity_unreliable)) 
to 0xFF9E9E9E.toInt();else-
>getString(R.string.state_label).format("Unknown")to0xFF9E9E9E.toInt()};textState.
text=statusText;indicator.setBackgroundColor(color)}} 
AstroCalculator.kt 
package com.example.poa 
import java.util.* 
import kotlin.math.* 
object AstroCalculator{ 
fun julianDate(date:Date):Double{val 
cal=Calendar.getInstance(TimeZone.getTimeZone("UTC"));cal.time=date;var 
Y=cal[Calendar.YEAR];var M=cal[Calendar.MONTH]+1;val 
D=cal[Calendar.DAY_OF_MONTH];val H=cal[Calendar.HOUR_OF_DAY];val 
Min=cal[Calendar.MINUTE];val S=cal[Calendar.SECOND];if(M<=2){Y--;M+=12};val 
A=floor(Y/100.0);val B=2-A+floor(A/4);val frac=(H+Min/60.0+S/3600.0)/24.0;return 
floor(365.25*(Y+4716))+floor(30.6001*(M+1))+D+B-1524.5+frac} 
fun gmstHours(jd:Double):Double{val T=(jd-2451545.0)/36525.0;var 
gmst=6.697374558+1.00273790935*(jd-
2451545.0)+0.0000258*T*T;gmst%=24.0;if(gmst<0)gmst+=24.0;return gmst} 
90 
482.ЧДТУ 252477 12 01 5
fun lstHours(gmst:Double,longitude:Double):Double{var 
lst=gmst+longitude/15.0;lst%=24.0;if(lst<0)lst+=24.0;return lst/15} 2 
fun normalizeHours(hours:Double):Double{var h=hours%24.0;if(h<0)h+=24.0;return h} 
fun formatRA(raHours:Double):String{val h=normalizeHours(raHours).toInt();val 
m=((normalizeHours(raHours)-h)*60).toInt();return "%02dh %02dm".format(h,m)} 
fun 
calculateHorizontalCoords(latitude:Double,lstHours:Double,starRaHours:Double,starD
ecDeg:Double):Pair<Double,Double>{val phi_rad=Math.toRadians(latitude);val 
dec_rad=Math.toRadians(starDecDeg);val 
raNormalized=normalizeHours(starRaHours);val LST_deg=lstHours*15.0;val 
RA_deg=raNormalized*15.0;var H_deg=LST_deg-RA_deg;H_deg=(H_deg+360.0)%360.0;val 
H_rad=Math.toRadians(H_deg);val 
sin_a=sin(phi_rad)*sin(dec_rad)+cos(phi_rad)*cos(dec_rad)*cos(H_rad);val 
altitude_rad=asin(sin_a);val altitude_deg=Math.toDegrees(altitude_rad);val y=-
sin(H_rad)*cos(dec_rad);val x=cos(phi_rad)*sin(dec_rad)-
sin(phi_rad)*cos(dec_rad)*cos(H_rad);var 
azimuth_deg=Math.toDegrees(atan2(y,x));azimuth_deg=(azimuth_deg+360.0)%360.0;retur
n Pair(azimuth_deg,altitude_deg)}} 
BaseActivity.kt 
package com.example.poa 
import android.content.Context 
import android.content.Intent 
import android.content.SharedPreferences 
import android.content.res.Configuration 
import android.os.Bundle 
import androidx.appcompat.widget.Toolbar 
import androidx.appcompat.app.ActionBarDrawerToggle 
import androidx.appcompat.app.AppCompatActivity 
import androidx.drawerlayout.widget.DrawerLayout 
import com.google.android.material.navigation.NavigationView 
import androidx.core.net.toUri 
import android.widget.Toast 
import java.util.Locale 
open class BaseActivity:AppCompatActivity(){ 
protected lateinit var drawerLayout:DrawerLayout 
protected lateinit var navView:NavigationView 
private lateinit var toggle:ActionBarDrawerToggle 
companion object{ 
const val PREFS_NAME="settings_prefs" 
const val PREF_LANG_KEY="language_code" 
const val LANG_UK="uk" 
const val LANG_EN="en" 
const val PREF_LAT_KEY="last_latitude" 
const val PREF_LON_KEY="last_longitude" 
fun getLocale(context:Context):Locale{val 
prefs:SharedPreferences=context.getSharedPreferences(PREFS_NAME,Context.MODE_PRIVA
TE);val langCode=prefs.getString(PREF_LANG_KEY,LANG_EN)?:LANG_EN;return 
Locale(langCode)} 
fun saveLocation(context:Context,latitude:Double,longitude:Double){val 
prefs:SharedPreferences=context.getSharedPreferences(PREFS_NAME,Context.MODE_PRIVA
TE);prefs.edit().apply{putLong(PREF_LAT_KEY,latitude.toBits());putLong(PREF_LON_KE
Y,longitude.toBits());apply()}} 
fun setLocale(context:Context,languageCode:String){val 
prefs:SharedPreferences=context.getSharedPreferences(PREFS_NAME,Context.MODE_PRIVA
TE);prefs.edit().putString(PREF_LANG_KEY,languageCode).apply();val 
locale=Locale(languageCode);Locale.setDefault(locale);val 
config=Configuration(context.resources.configuration);config.setLocale(locale);con
text.createConfigurationContext(config);context.resources.updateConfiguration(conf
ig,context.resources.displayMetrics)} 
fun loadLocation(context:Context):Pair<Double,Double>{val 
prefs:SharedPreferences=context.getSharedPreferences(PREFS_NAME,Context.MODE_PRIVA
91 
482.ЧДТУ 252477 12 01 6 
TE);val latBits=prefs.getLong(PREF_LAT_KEY,0L);val 
lonBits=prefs.getLong(PREF_LON_KEY,0L);val lat=Double.fromBits(latBits);val 
lon=Double.fromBits(lonBits);return Pair(lat,lon)}} 
override fun attachBaseContext(newBase:Context){val locale=getLocale(newBase);val 
config=Configuration(newBase.resources.configuration);config.setLocale(locale);val 
context=newBase.createConfigurationContext(config);super.attachBaseContext(context
)} 
override fun 
setContentView(layoutResID:Int){super.setContentView(layoutResID);setupDrawer()} 
protected fun updateLocaleAndRestart(newLang:String){setLocale(this,newLang);val 
message=if(newLang==LANG_UK){getString(R.string.language_set_ukrainian)}else{getSt
ring(R.string.language_set_english)};Toast.makeText(this,message,Toast.LENGTH_SHOR
T).show();val intent=intent;finish();startActivity(intent)} 
private fun setupDrawer(){val 
toolbar:Toolbar=findViewById(R.id.toolbar);setSupportActionBar(toolbar);drawerLayo
ut=findViewById(R.id.drawer_layout);navView=findViewById(R.id.nav_view);toggle=Act
ionBarDrawerToggle(this,drawerLayout,toolbar,R.string.navigation_drawer_open,R.str
ing.navigation_drawer_close);drawerLayout.addDrawerListener(toggle);toggle.syncSta
te();navView.setNavigationItemSelectedListener{menuItem-
>when(menuItem.itemId){R.id.nav_info-
>startActivity(Intent(this,StarCatalogActivity::class.java));R.id.nav_gyroscope-
>startActivity(Intent(this,GyroscopeActivity::class.java));R.id.nav_main-
>startActivity(Intent(this,MainActivity::class.java));R.id.nav_accel-
>startActivity(Intent(this,AccelerometerActivity::class.java));R.id.nav_magnet-
>startActivity(Intent(this,MagnetometerActivity::class.java));R.id.nav_polaris-
>startActivity(Intent(this,PolarisFinderActivity::class.java));R.id.nav_time_sider
eal-
>startActivity(Intent(this,TimeSiderealActivity::class.java));R.id.nav_language-
>{val currentLang=getLocale(this).language;val 
newLang=if(currentLang==LANG_UK)LANG_EN else 
LANG_UK;updateLocaleAndRestart(newLang);return@setNavigationItemSelectedListener 
true};R.id.nav_website->{val 
intent=Intent(Intent.ACTION_VIEW);intent.data="https://t.me/Holscore".toUri();star
tActivity(intent);true};else->false};drawerLayout.closeDrawer(navView);true}}} 
EarthGLSurfaceView.kt 
package com.example.poa 
import android.content.Context 
import android.opengl.GLSurfaceView 
import android.util.AttributeSet 
import android.view.MotionEvent 
import android.view.ScaleGestureDetector 
import android.util.Log 
class EarthGLSurfaceView @JvmOverloads 
constructor(context:Context,attrs:AttributeSet?=null):GLSurfaceView(context,attrs)
{ 
private val renderer:EarthRenderer 
private val scaleDetector:ScaleGestureDetector 
init{setEGLContextClientVersion(2);renderer=EarthRenderer(context);setRenderer(ren
derer);renderMode=RENDERMODE_WHEN_DIRTY;preserveEGLContextOnPause=true;scaleDetect
or=ScaleGestureDetector(context,object:ScaleGestureDetector.SimpleOnScaleGestureLi
stener(){override fun onScale(detector:ScaleGestureDetector):Boolean{val 
scaleFactor=detector.scaleFactor;queueEvent{renderer.updateZoom(scaleFactor)};requ
estRender();return true}})}private var previousX=0fprivate var 
previousY=0foverridefunonTouchEvent(event:MotionEvent):Boolean{scaleDetector.onTou
chEvent(event);when(event.action 
andMotionEvent.ACTION_MASK){MotionEvent.ACTION_MOVE->{val x=event.x;val 
y=event.y;val dx=x-previousX;val dy=y-
previousY;if(event.pointerCount==1){queueEvent{try{renderer.angleX+=dx*0.4f;render
er.angleY+=dy*0.4f}catch(e:Exception){Log.e("EarthGLSurfaceView","Error updating 
angles",e)}};requestRender()}};previousX=event.x;previousY=event.y;return true} 
fun 
setTiltAngle(angle:Float){queueEvent{try{renderer.tiltAngle=angle}catch(e:Exceptio
n){Log.e("EarthGLSurfaceView","Error setting tilt angle",e)}};requestRender()} 
92 
482.ЧДТУ 252477 12 01 7
fun 
setMarker(lat:Double,lon:Double){queueEvent{try{renderer.setMarker(lat,lon)}ca2t ch(
e:Exception){Log.e("EarthGLSurfaceView","Error setting 
marker",e)}};requestRender()}} 
EarthRenderer.kt 
package com.example.poa 
import android.content.Context 
import android.graphics.BitmapFactory 
import android.opengl.GLES20 
import android.opengl.GLSurfaceView 
import android.opengl.GLUtils 
import android.opengl.Matrix 
import java.nio.ByteBuffer 
import java.nio.ByteOrder 
import java.nio.FloatBuffer 
import javax.microedition.khronos.egl.EGLConfig 
import javax.microedition.khronos.opengles.GL10 
import kotlin.math.* 
class EarthRenderer(private val context:Context):GLSurfaceView.Renderer{ 
private var mProgramTex=0 
private var mProgramPoint=0 
private lateinit var vertexBuffer:FloatBuffer 
private lateinit var normalBuffer:FloatBuffer 
private lateinit var texBuffer:FloatBuffer 
private var vertexCount=0 
private val mvpMatrix=FloatArray(16) 
private val projectionMatrix=FloatArray(16) 
private val viewMatrix=FloatArray(16) 
private val modelMatrix=FloatArray(16) 
private var textureId=0 
private val EARTH_RADIUS=2.0f 
private var currentDistance=5f 
private val MIN_DISTANCE=2.5f 
private val MAX_DISTANCE=15.0f 
private val MARKER_OFFSET=0.02f 
@Volatile var angleX=0f 
@Volatile var angleY=0f 
@Volatile var tiltAngle=23.44f 
@Volatile private var markerLat:Double?=null 
@Volatile private var markerLon:Double?=null 
private var markerPos=floatArrayOf(0f,0f,0f) 
private val eyeX=0f 
private val eyeY=0f 
@Volatile var markerSize=10f 
fun 
updateZoom(scaleFactor:Float){currentDistance/=scaleFactor;if(currentDistance<MIN_
DISTANCE)currentDistance=MIN_DISTANCE;if(currentDistance>MAX_DISTANCE)currentDista
nce=MAX_DISTANCE} 
fun 
setMarker(lat:Double,lon:Double){markerLat=lat;markerLon=lon;markerPos=convertLatL
onToXYZ(lat,lon);angleX=-180f-lon.toFloat();angleY=0f} 
private fun convertLatLonToXYZ(lat:Double,lon:Double):FloatArray{val 
radius=EARTH_RADIUS+MARKER_OFFSET;val latRad=Math.toRadians(lat);val 
lonRad=Math.toRadians(lon)+PI;val y=(radius*sin(latRad)).toFloat();val 
rXZ=radius*cos(latRad);val x=(rXZ*sin(lonRad)).toFloat();val 
z=(rXZ*cos(lonRad)).toFloat();return floatArrayOf(x,y,z)} 
override fun 
onSurfaceCreated(gl:GL10?,config:EGLConfig?){GLES20.glClearColor(0.05f,0.05f,0.1f,
1f);GLES20.glEnable(GLES20.GL_DEPTH_TEST);GLES20.glEnable(GLES20.GL_BLEND);GLES20.
glBlendFunc(GLES20.GL_SRC_ALPHA,GLES20.GL_ONE_MINUS_SRC_ALPHA);val(verts,norms,uvs
)=createSphere(EARTH_RADIUS,72,72);vertexCount=verts.size/3;vertexBuffer=allocateB
93 
482.ЧДТУ 252477 12 01 8
uffer(verts);normalBuffer=allocateBuffer(norms);texBuffer=allocateBuffer(uvs);mPro
gramTex=linkShaders(vsSphereShader,fsSphereShader);mProgramPoint=linkShaders(v2s Mar
kerShader,fsMarkerShader);textureId=loadTexture(R.drawable.earth_day)} 
override fun 
onSurfaceChanged(gl:GL10?,width:Int,height:Int){GLES20.glViewport(0,0,width,height
);val ratio=width.toFloat()/height.toFloat();Matrix.frustumM(projectionMatrix,0,-
ratio,ratio,-1f,1f,2f,MAX_DISTANCE+1f)} 
override fun onDrawFrame(gl:GL10?){GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT or 
GLES20.GL_DEPTH_BUFFER_BIT);Matrix.setLookAtM(viewMatrix,0,eyeX,eyeY,currentDistan
ce,0f,0f,0f,0f,1f,0f);Matrix.setIdentityM(modelMatrix,0);Matrix.rotateM(modelMatri
x,0,tiltAngle,1f,0f,0f);Matrix.rotateM(modelMatrix,0,angleY,1f,0f,0f);Matrix.rotat
eM(modelMatrix,0,angleX,0f,1f,0f);val 
mvMatrix=FloatArray(16);Matrix.multiplyMM(mvMatrix,0,viewMatrix,0,modelMatrix,0);M
atrix.multiplyMM(mvpMatrix,0,projectionMatrix,0,mvMatrix,0);renderSphereWithTextur
e();renderMarker()} 
private fun renderSphereWithTexture(){GLES20.glUseProgram(mProgramTex);val 
posHandle=GLES20.glGetAttribLocation(mProgramTex,"aPosition");val 
normHandle=GLES20.glGetAttribLocation(mProgramTex,"aNormal");val 
texHandle=GLES20.glGetAttribLocation(mProgramTex,"aTexCoord");val 
mvpHandle=GLES20.glGetUniformLocation(mProgramTex,"uMVPMatrix");val 
texSampler=GLES20.glGetUniformLocation(mProgramTex,"uTexture");GLES20.glEnableVert
exAttribArray(posHandle);GLES20.glVertexAttribPointer(posHandle,3,GLES20.GL_FLOAT,
false,0,vertexBuffer);GLES20.glEnableVertexAttribArray(normHandle);GLES20.glVertex
AttribPointer(normHandle,3,GLES20.GL_FLOAT,false,0,normalBuffer);GLES20.glEnableVe
rtexAttribArray(texHandle);GLES20.glVertexAttribPointer(texHandle,2,GLES20.GL_FLOA
T,false,0,texBuffer);GLES20.glUniformMatrix4fv(mvpHandle,1,false,mvpMatrix,0);GLES
20.glActiveTexture(GLES20.GL_TEXTURE0);GLES20.glBindTexture(GLES20.GL_TEXTURE_2D,t
extureId);GLES20.glUniform1i(texSampler,0);GLES20.glDrawArrays(GLES20.GL_TRIANGLES
,0,vertexCount);GLES20.glDisableVertexAttribArray(posHandle);GLES20.glDisableVerte
xAttribArray(normHandle);GLES20.glDisableVertexAttribArray(texHandle)} 
private fun 
renderMarker(){if(markerLat==null||markerLon==null)return;GLES20.glDisable(GLES20.
GL_DEPTH_TEST);GLES20.glUseProgram(mProgramPoint);val 
pointHandle=GLES20.glGetAttribLocation(mProgramPoint,"aPosition");val 
mvpHandle=GLES20.glGetUniformLocation(mProgramPoint,"uMVPMatrix");val 
sizeHandle=GLES20.glGetUniformLocation(mProgramPoint,"uPointSize");val 
pointBuffer=allocateBuffer(markerPos);GLES20.glEnableVertexAttribArray(pointHandle
);GLES20.glVertexAttribPointer(pointHandle,3,GLES20.GL_FLOAT,false,0,pointBuffer);
GLES20.glUniformMatrix4fv(mvpHandle,1,false,mvpMatrix,0);GLES20.glUniform1f(sizeHa
ndle,markerSize);GLES20.glDrawArrays(GLES20.GL_POINTS,0,1);GLES20.glDisableVertexA
ttribArray(pointHandle);GLES20.glEnable(GLES20.GL_DEPTH_TEST)} 
private fun 
createSphere(radius:Float,latBands:Int,longBands:Int):Triple<FloatArray,FloatArray
,FloatArray>{val vertices=mutableListOf<Float>();val 
normals=mutableListOf<Float>();val texCoords=mutableListOf<Float>();for(lat in 0 
until latBands){val theta1=lat*PI/latBands;val theta2=(lat+1)*PI/latBands;for(lon 
in 0 until longBands){val phi1=lon*2*PI/longBands;val 
phi2=(lon+1)*2*PI/longBands;val p1=v(theta1,phi1,radius);val 
p2=v(theta1,phi2,radius);val p3=v(theta2,phi2,radius);val 
p4=v(theta2,phi1,radius);val t1=uv(theta1,phi1);val t2=uv(theta1,phi2);val 
t3=uv(theta2,phi2);val 
t4=uv(theta2,phi1);vertices.addAll(listOf(p1[0],p1[1],p1[2],p2[0],p2[1],p2[2],p3[0
],p3[1],p3[2]));vertices.addAll(listOf(p1[0],p1[1],p1[2],p3[0],p3[1],p3[2],p4[0],p
4[1],p4[2]));normals.addAll(listOf(p1[0],p1[1],p1[2],p2[0],p2[1],p2[2],p3[0],p3[1]
,p3[2]));normals.addAll(listOf(p1[0],p1[1],p1[2],p3[0],p3[1],p3[2],p4[0],p4[1],p4[
2]));texCoords.addAll(listOf(t1[0],t1[1],t2[0],t2[1],t3[0],t3[1]));texCoords.addAl
l(listOf(t1[0],t1[1],t3[0],t3[1],t4[0],t4[1]))}}return 
Triple(vertices.toFloatArray(),normals.toFloatArray(),texCoords.toFloatArray())} 
private fun v(theta:Double,phi:Double,radius:Float):FloatArray{val 
y=cos(theta);val x=sin(theta)*sin(phi);val z=sin(theta)*cos(phi);return 
floatArrayOf((x*radius).toFloat(),(y*radius).toFloat(),(z*radius).toFloat())} 
private fun uv(theta:Double,phi:Double):FloatArray{var 
s=(phi/(2*PI)).toFloat();if(s<0f)s=0f;if(s>1f)s=1f;val 
t=(theta/PI).toFloat();return floatArrayOf(s,t)} 
94 
482.ЧДТУ 252477 12 01 9 
private fun 
allocateBuffer(array:FloatArray):FloatBuffer=ByteBuffer.allocateDirect(array.size*
4).order(ByteOrder.nativeOrder()).asFloatBuffer().apply{put(array);position(0)} 
private fun linkShaders(vs:String,fs:String):Int{val 
v=compileShader(GLES20.GL_VERTEX_SHADER,vs);val 
f=compileShader(GLES20.GL_FRAGMENT_SHADER,fs);val 
program=GLES20.glCreateProgram();GLES20.glAttachShader(program,v);GLES20.glAttachS
hader(program,f);GLES20.glLinkProgram(program);return program} 
private fun 
compileShader(type:Int,code:String):Int=GLES20.glCreateShader(type).also{GLES20.gl
ShaderSource(it,code);GLES20.glCompileShader(it)} 
private fun loadTexture(resId:Int):Int{val 
idArr=IntArray(1);GLES20.glGenTextures(1,idArr,0);val 
texId=idArr[0];GLES20.glBindTexture(GLES20.GL_TEXTURE_2D,texId);val 
opts=BitmapFactory.Options().apply{inScaled=false};val 
bmp=BitmapFactory.decodeResource(context.resources,resId,opts)?:return 
0;GLUtils.texImage2D(GLES20.GL_TEXTURE_2D,0,bmp,0);bmp.recycle();GLES20.glTexParam
eteri(GLES20.GL_TEXTURE_2D,GLES20.GL_TEXTURE_MIN_FILTER,GLES20.GL_LINEAR);GLES20.g
lTexParameteri(GLES20.GL_TEXTURE_2D,GLES20.GL_TEXTURE_MAG_FILTER,GLES20.GL_LINEAR)
;GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D,GLES20.GL_TEXTURE_WRAP_S,GLES20.GL_RE
PEAT);GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D,GLES20.GL_TEXTURE_WRAP_T,GLES20.
GL_CLAMP_TO_EDGE);return texId} 
private val vsSphereShader="""attribute vec3 aPosition;attribute vec3 
aNormal;attribute vec2 aTexCoord;uniform mat4 uMVPMatrix;varying vec2 
vTexCoord;void 
main(){vTexCoord=aTexCoord;gl_Position=uMVPMatrix*vec4(aPosition,1.0);}""" 
private val fsSphereShader="""precision mediump float;varying vec2 
vTexCoord;uniform sampler2D uTexture;void 
main(){gl_FragColor=texture2D(uTexture,vec2(vTexCoord.x,vTexCoord.y));}""" 
private val vsMarkerShader="""attribute vec3 aPosition;uniform mat4 
uMVPMatrix;uniform float uPointSize;void 
main(){gl_Position=uMVPMatrix*vec4(aPosition,1.0);gl_PointSize=uPointSize;}""" 
private val fsMarkerShader="""precision mediump float;void main(){float 
dist=length(gl_PointCoord-
vec2(0.5));if(dist>0.5)discard;gl_FragColor=vec4(1.0,0.0,0.0,1.0);}"""} 
GyroscopeActivity.kt 
package com.example.poa 
import android.annotation.SuppressLint 
import android.hardware.* 
import android.os.Bundle 
import android.widget.TextView 
import kotlin.math.* 
class GyroscopeActivity:BaseActivity(),SensorEventListener{ 
private lateinit var sm:SensorManager 
private var g:Sensor?=null 
private lateinit var xT:TextView 
private lateinit var yT:TextView 
private lateinit var zT:TextView 
private lateinit var sT:TextView 
override fun 
onCreate(b:Bundle?){super.onCreate(b);setContentView(R.layout.activity_gyro);xT=fi
ndViewById(R.id.textGyroX);yT=findViewById(R.id.textGyroY);zT=findViewById(R.id.te
xtGyroZ);sT=findViewById(R.id.textRotationState);sm=getSystemService(SENSOR_SERVIC
E) as 
SensorManager;g=sm.getDefaultSensor(Sensor.TYPE_GYROSCOPE);if(g==null)sT.text=getS
tring(R.string.accelerometer_not_found).replace("Accelerometer","Gyroscope")} 
override fun 
onResume(){super.onResume();g?.let{sm.registerListener(this,it,SensorManager.SENSO
R_DELAY_GAME)}} 
override fun onPause(){super.onPause();sm.unregisterListener(this)} 
@SuppressLint("SetTextI18n") 
95 
482.ЧДТУ 252477 12 01 10 
override fun 
onSensorChanged(e:SensorEvent){if(e.sensor.type!=Sensor.TYPE_GYROSCOPE)return;val 
x=e.values[0];val y=e.values[1];val 
z=e.values[2];xT.text=getString(R.string.rotation_x).format(x);yT.text=getString(R
.string.rotation_y).format(y);zT.text=getString(R.string.rotation_z).format(z);val 
m=when{abs(x)>2->if(x>0)getString(R.string.tilt_forward)else 
getString(R.string.tilt_back);abs(y)>2->if(y>0)getString(R.string.tilt_left)else 
getString(R.string.tilt_right);abs(z)>2-
>if(z>0)getString(R.string.rotate_forward)else 
getString(R.string.rotate_back);else-
>getString(R.string.phone_almost_stationary)};val mag=sqrt(x*x+y*y+z*z);val 
i=when{mag>5->getString(R.string.intensity_high);mag>1-
>getString(R.string.intensity_medium);mag>0.1-
>getString(R.string.intensity_low);else-
>getString(R.string.intensity_zero)};sT.text="$m\n $i"} 
override fun onAccuracyChanged(s:Sensor?,a:Int){}} 
MagnetometerActivity.kt 
package com.example.poa 
import android.hardware.* 
import android.os.Bundle 
import android.widget.TextView 
import kotlin.math.atan2 
class MagnetometerActivity:BaseActivity(),SensorEventListener{ 
private lateinit var sm:SensorManager 
private var m:Sensor?=null 
private lateinit var xT:TextView 
private lateinit var yT:TextView 
private lateinit var zT:TextView 
private lateinit var dT:TextView 
override fun 
onCreate(b:Bundle?){super.onCreate(b);setContentView(R.layout.activity_magnetomete
r);xT=findViewById(R.id.textMagX);yT=findViewById(R.id.textMagY);zT=findViewById(R
.id.textMagZ);dT=findViewById(R.id.textCompassState);sm=getSystemService(SENSOR_SE
RVICE) as 
SensorManager;m=sm.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);if(m==null)dT.text
=getString(R.string.accelerometer_not_found).replace("Accelerometer","Magnetometer
")} 
override fun 
onResume(){super.onResume();m?.let{sm.registerListener(this,it,SensorManager.SENSO
R_DELAY_GAME)}} 
override fun onPause(){super.onPause();sm.unregisterListener(this)} 
override fun 
onSensorChanged(e:SensorEvent){if(e.sensor.type!=Sensor.TYPE_MAGNETIC_FIELD)return
;val x=e.values[0];val y=e.values[1];val 
z=e.values[2];xT.text=getString(R.string.magnetometer_x).format(x);yT.text=getStri
ng(R.string.magnetometer_y).format(y);zT.text=getString(R.string.magnetometer_z).f
ormat(z);val a=(Math.toDegrees(atan2(y,x).toDouble())+360)%360;val d=when{a<22.5-
>getString(R.string.direction_north);a<67.5-
>getString(R.string.direction_northeast);a<112.5-
>getString(R.string.direction_east);a<157.5-
>getString(R.string.direction_southeast);a<202.5-
>getString(R.string.direction_south);a<247.5-
>getString(R.string.direction_southwest);a<292.5-
>getString(R.string.direction_west);a<337.5-
>getString(R.string.direction_northwest);else-
>getString(R.string.direction_north)};dT.text=getString(R.string.compass_info,d,a)
} 
override fun onAccuracyChanged(s:Sensor?,a:Int){}} 
MainActivity.kt 
96 
482.ЧДТУ 252477 12 01 11 
package com.example.poa 
import android.Manifest 
import android.content.pm.PackageManager 
import android.graphics.ImageFormat 
import android.hardware.* 
import android.hardware.camera2.* 
import android.media.Image 
import android.media.ImageReader 
import android.os.* 
import android.util.Log 
import android.util.Size 
import android.view.* 
import android.view.animation.AlphaAnimation 
import android.widget.* 
import androidx.appcompat.app.AlertDialog 
import androidx.core.app.ActivityCompat 
import androidx.core.content.ContextCompat 
import com.google.android.gms.location.* 
import org.json.JSONObject 
import java.io.File 
import java.io.FileOutputStream 
import java.util.* 
import kotlin.math.floor 
class MainActivity:BaseActivity(),SensorEventListener{ 
private lateinit var sm:SensorManager 
private var a:Sensor?=null 
private var g:Sensor?=null 
private var m:Sensor?=null 
private var av=FloatArray(3) 
private var gv=FloatArray(3) 
private var mv=FloatArray(3) 
private lateinit var cm:CameraManager 
private var cd:CameraDevice?=null 
private var cs:CameraCaptureSession?=null 
private lateinit var ir:ImageReader 
private lateinit var ch:CameraCharacteristics 
private var pi=0 
private var tp=60 
private var itv=500L 
private var ss:Size?=null 
private lateinit var ct:TextView 
private lateinit var pv:TextureView 
private lateinit var fv:View 
private lateinit var sd:File 
private lateinit var btn:Button 
private var lcr:TotalCaptureResult?=null 
private var anim:AlphaAnimation?=null 
private val h=Handler(Looper.getMainLooper()) 
private var r:Runnable?=null 
private var cap=false 
private lateinit var fl:FusedLocationProviderClient 
override fun 
onCreate(b:Bundle?){super.onCreate(b);setContentView(R.layout.activity_main);ct=fi
ndViewById(R.id.cameraText);pv=findViewById(R.id.cameraPreview);fv=findViewById(R.
id.flashView);btn=findViewById(R.id.button);sd=File(getExternalFilesDir(null),"Cap
tures");sd.mkdirs();sm=getSystemService(SENSOR_SERVICE) as 
SensorManager;a=sm.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);g=sm.getDefaultSens
or(Sensor.TYPE_GYROSCOPE);m=sm.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);cm=get
SystemService(CAMERA_SERVICE) as 
CameraManager;fl=LocationServices.getFusedLocationProviderClient(this);btn.setOnCl
ickListener{if(!cap)chk()}} 
override fun 
onResume(){super.onResume();a?.let{sm.registerListener(this,it,SensorManager.SENSO
97 
482.ЧДТУ 252477 12 01 12 
R_DELAY_FASTEST)};g?.let{sm.registerListener(this,it,SensorManager.SENSOR_DELAY_FA
STEST)};m?.let{sm.registerListener(this,it,SensorManager.SENSOR_DELAY_FASTEST)}} 
override fun 
onPause(){super.onPause();sm.unregisterListener(this);r?.let{h.removeCallbacks(it)
};cls()} 
override fun 
onSensorChanged(e:SensorEvent){when(e.sensor.type){Sensor.TYPE_ACCELEROMETER-
>av=e.values.clone();Sensor.TYPE_GYROSCOPE-
>gv=e.values.clone();Sensor.TYPE_MAGNETIC_FIELD->mv=e.values.clone()}} 
override fun onAccuracyChanged(s:Sensor?,a:Int){} 
private fun chk(){val 
p=arrayOf(Manifest.permission.CAMERA,Manifest.permission.ACCESS_FINE_LOCATION);val 
ng=p.filter{ContextCompat.checkSelfPermission(this,it)!=PackageManager.PERMISSION_
GRANTED};if(ng.isNotEmpty())ActivityCompat.requestPermissions(this,ng.toTypedArray
(),101)else{val 
id=cm.cameraIdList.firstOrNull()?:return;ch=cm.getCameraCharacteristics(id);val 
map=ch.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);val 
sz=map?.getOutputSizes(ImageFormat.RAW_SENSOR)?.toList()?:map?.getOutputSizes(Imag
eFormat.JPEG)?.toList()?:listOf(Size(4032,3024));ss=ss?:sz.first();dlg(sz){open(id
,sz)}}} 
private fun 
open(id:String,sz:List<Size>){cap=true;btn.isEnabled=false;pi=0;proc();val 
s=sz.find{it==ss}?:sz.first();ir=ImageReader.newInstance(s.width,s.height,ImageFor
mat.RAW_SENSOR,3);ir.setOnImageAvailableListener({r->val 
i=r.acquireNextImage()?:return@setOnImageAvailableListener;save(i)},h);if(Activity
Compat.checkSelfPermission(this,Manifest.permission.CAMERA)!=PackageManager.PERMIS
SION_GRANTED)return;cm.openCamera(id,object:CameraDevice.StateCallback(){override 
fun onOpened(d:CameraDevice){cd=d;start()};override fun 
onDisconnected(d:CameraDevice){sc(d)};override fun 
onError(d:CameraDevice,e:Int){sc(d)}},h)} 
private fun 
sc(d:CameraDevice?){try{d?.close()}catch(_:Exception){};if(d==cd)cd=null} 
private fun cls(){try{cs?.close()}catch(_:Exception){};cs=null;sc(cd)} 
private fun start(){val 
t=pv.surfaceTexture?:return;t.setDefaultBufferSize(1920,1080);val 
s=Surface(t);cd?.createCaptureSession(listOf(s,ir.surface),object:CameraCaptureSes
sion.StateCallback(){override fun 
onConfigured(ss:CameraCaptureSession){cs=ss;prev(s);tim()};override fun 
onConfigureFailed(s:CameraCaptureSession){}},h)} 
private fun prev(s:Surface){val 
r=cd!!.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW).apply{addTarget(s)};cs!
!.setRepeatingRequest(r.build(),object:CameraCaptureSession.CaptureCallback(){over
ride fun 
onCaptureCompleted(s:CameraCaptureSession,r:CaptureRequest,res:TotalCaptureResult)
{lcr=res}},h)} 
private fun tim(){r=object:Runnable{override fun 
run(){if(pi<tp){shot();pi++;if(pi<tp){h.postDelayed(this,itv);upd()}else 
done()}}};h.post(r!!)} 
private fun shot(){val d=cd?:return;val 
r=d.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE).apply{addTarget(ir.s
urface)};cs?.capture(r.build(),object:CameraCaptureSession.CaptureCallback(){overr
ide fun 
onCaptureCompleted(s:CameraCaptureSession,r:CaptureRequest,res:TotalCaptureResult)
{lcr=res}},h);flash()} 
private fun save(i:Image){val r=lcr?:run{i.close();return};val 
ts=System.currentTimeMillis();val 
f=File(sd,"IMG_$ts.dng");try{FileOutputStream(f).use{o-
>DngCreator(ch,r).use{it.writeImage(o,i)}}}catch(e:Exception){Log.e("SAVE",e.messa
ge?:"")}finally{i.close()};if(ActivityCompat.checkSelfPermission(this,Manifest.per
mission.ACCESS_FINE_LOCATION)==PackageManager.PERMISSION_GRANTED)fl.lastLocation.a
ddOnSuccessListener{loc->json(ts,loc)}.addOnFailureListener{json(ts,null)}else 
json(ts,null);upd()} 
private fun json(ts:Long,l:android.location.Location?){val 
j=JSONObject().apply{put("timestamp",ts);put("photo_index",pi);put("accel",av.toLi
98 
482.ЧДТУ 252477 12 01 13 
st());put("gyro",gv.toList());put("magnet",mv.toList());if(l!=null){put("latitude"
,l.latitude);put("longitude",l.longitude);put("lst",lst(l.longitude))}else{put("la
titude",JSONObject.NULL);put("longitude",JSONObject.NULL);put("lst",JSONObject.NUL
L)}};try{File(sd,"META_$ts.json").writeText(j.toString())}catch(e:Exception){Log.e
("SAVE",e.message?:"")}} 
private fun lst(lon:Double):String{val 
c=Calendar.getInstance(TimeZone.getTimeZone("UTC"));val jd=jd(c);val g=gs(jd);val 
h=(g+lon/15)%24;return String.format(Locale.US,"%.2f",if(h<0)h+24 else h)} 
private fun jd(c:Calendar):Double{val y=c.get(Calendar.YEAR);val 
m=c.get(Calendar.MONTH)+1;val d=c.get(Calendar.DAY_OF_MONTH);val 
h=c.get(Calendar.HOUR_OF_DAY);val mi=c.get(Calendar.MINUTE);val 
s=c.get(Calendar.SECOND);val j=367*y-
floor((7*(y+floor((m+9)/12.0)))/4.0).toInt()+floor(275*m/9.0).toInt()+d+1721013.5;
return j+(h+mi/60.0+s/3600.0)/24.0} 
private fun gs(jd:Double):Double{val T=(jd-2451545.0)/36525.0;var 
g=280.46061837+360.98564736629*(jd-2451545.0)+0.000387933*T*T-
T*T*T/38710000.0;g%=360;return g/15} 
private fun flash(){val 
a=AlphaAnimation(0f,0.8f).apply{duration=120;repeatMode=AlphaAnimation.REVERSE;rep
eatCount=1};runOnUiThread{fv.startAnimation(a)}} 
private fun 
proc(){runOnUiThread{ct.text=getString(R.string.shooting_progress,pi,tp);anim=Alph
aAnimation(0f,1f).apply{duration=500;repeatMode=AlphaAnimation.REVERSE;repeatCount
=AlphaAnimation.INFINITE};ct.startAnimation(anim)}} 
private fun 
upd(){runOnUiThread{if(cap)ct.text=getString(R.string.shooting_progress,pi,tp)}} 
private fun 
done(){runOnUiThread{btn.isEnabled=true;cap=false;anim?.cancel();ct.clearAnimation
();ct.text=getString(R.string.done)}} 
private fun dlg(sz:List<Size>,ok:()->Unit){val 
o=sz.map{"${it.width}x${it.height}"}.toTypedArray();val 
i=sz.indexOf(ss?:sz[0]);val 
b=AlertDialog.Builder(this);b.setTitle(getString(R.string.camera_settings_title));
b.setSingleChoiceItems(o,i){_,w->ss=sz[w]};val 
v=layoutInflater.inflate(R.layout.dialog_settings,null);val 
inI=v.findViewById<EditText>(R.id.intervalInput);val 
inM=v.findViewById<EditText>(R.id.maxPhotosInput);inI.setText(itv.toString());inM.
setText(tp.toString());b.setView(v);b.setPositiveButton(getString(R.string.camera_
settings_ok)){d,_-
>itv=inI.text.toString().toLongOrNull()?.coerceIn(100L,60000L)?:500L;tp=inM.text.t
oString().toIntOrNull()?.coerceIn(1,1000)?:60;d.dismiss();ok()};b.setNegativeButto
n(getString(R.string.camera_settings_cancel)){d,_->d.dismiss()};b.show()}} 
PolarisFinderActivity.kt 
package com.example.poa 
import android.content.Context 
import android.graphics.PorterDuff 
import android.hardware.* 
import android.os.Bundle 
import android.view.animation.AlphaAnimation 
import android.widget.ImageView 
import android.widget.TextView 
import kotlin.math.* 
class PolarisFinderActivity:BaseActivity(),SensorEventListener{ 
private lateinit var sm:SensorManager 
private var ad=FloatArray(3) 
private var md=FloatArray(3) 
private var a:Sensor?=null 
private var m:Sensor?=null 
private var tc=0 
private lateinit var ar:ImageView 
private lateinit var at:TextView 
99 
482.ЧДТУ 252477 12 01 14 
private lateinit var g:ImageView 
private var f=false 
override fun 
onCreate(b:Bundle?){super.onCreate(b);setContentView(R.layout.activity_polaris_fin
der);sm=getSystemService(Context.SENSOR_SERVICE) as 
SensorManager;a=sm.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);m=sm.getDefaultSens
or(Sensor.TYPE_MAGNETIC_FIELD);ar=findViewById(R.id.arrowView);at=findViewById(R.i
d.angleText);g=findViewById(R.id.glowEffect);tc=at.currentTextColor;g.alpha=0f} 
override fun 
onResume(){super.onResume();a?.let{sm.registerListener(this,it,SensorManager.SENSO
R_DELAY_GAME)};m?.let{sm.registerListener(this,it,SensorManager.SENSOR_DELAY_GAME)
}} 
override fun onPause(){super.onPause();sm.unregisterListener(this)} 
override fun 
onSensorChanged(e:SensorEvent){when(e.sensor.type){Sensor.TYPE_ACCELEROMETER-
>ad=e.values.clone();Sensor.TYPE_MAGNETIC_FIELD->md=e.values.clone()};val 
r=FloatArray(9);val 
i=FloatArray(9);if(!SensorManager.getRotationMatrix(r,i,ad,md))return;val 
o=FloatArray(3);SensorManager.getOrientation(r,o);val 
az=Math.toDegrees(o[0].toDouble()).toFloat();val 
n=(az+360)%360;at.text=getString(R.string.current_azimuth).format(n.roundToInt());
ar.rotation=-n;val d=min(abs(n),abs(n-360f));if(d<10){if(!f){f=true;on()}}else 
if(f){f=false;off()}} 
override fun onAccuracyChanged(s:Sensor?,a:Int){} 
private fun on(){val 
an=AlphaAnimation(0.7f,1f).apply{duration=600;repeatCount=AlphaAnimation.INFINITE;
repeatMode=AlphaAnimation.REVERSE};val 
c=getColor(R.color.burnt_orange);g.setColorFilter(c,PorterDuff.Mode.SRC_IN);ar.set
ColorFilter(c,PorterDuff.Mode.SRC_IN);at.setTextColor(c);g.startAnimation(an);g.al
pha=1f} 
private fun off(){g.clearAnimation(); 
g.alpha=0f;g.colorFilter=null;ar.colorFilter=null;at.setTextColor(tc)}} 
Star.kt 
data class Star(val name:String,val ra:Double,val dec:Double,val type:String) 
StarAdapter.kt 
package com.example.poa 
import Star 
import android.view.* 
import android.widget.TextView 
import androidx.recyclerview.widget.RecyclerView 
class StarAdapter(private var s:MutableList<Star>,private val c:(Star)-
>Unit,private val v:((Star)-
>Boolean)?=null):RecyclerView.Adapter<StarAdapter.H>(){ 
inner class H(w:View):RecyclerView.ViewHolder(w){val 
t:TextView=w.findViewById(android.R.id.text1)} 
override fun 
onCreateViewHolder(p:ViewGroup,vT:Int)=H(LayoutInflater.from(p.context).inflate(an
droid.R.layout.simple_list_item_1,p,false)) 
override fun getItemCount()=s.size 
override fun onBindViewHolder(h:H,p:Int){val 
st=s[p];h.t.text=if(v?.invoke(st)==true)"⭐${st.name}"else 
st.name;h.itemView.setOnClickListener{c(st)}} 
fun updateData(n:List<Star>){s.clear();s.addAll(n);notifyDataSetChanged()} 
fun addData(n:List<Star>){val 
i=s.size;s.addAll(n);notifyItemRangeInserted(i,n.size)}} 
 
StarCatalogActivity.kt 
100 
482.ЧДТУ 252477 12 01 15 
package com.example.poa 
import Star 
import android.graphics.drawable.ColorDrawable 
import android.os.Bundle 
import android.text.* 
import android.view.View 
import android.widget.* 
import androidx.appcompat.app.AlertDialog 
import androidx.core.content.ContextCompat 
import androidx.lifecycle.lifecycleScope 
import androidx.recyclerview.widget.* 
import com.example.poa.db.StarDbHelper 
import kotlinx.coroutines.* 
import java.util.* 
import kotlin.math.floor 
class StarCatalogActivity:BaseActivity(){ 
private lateinit var r:RecyclerView 
private lateinit var s:EditText 
private lateinit var e:TextView 
private lateinit var p:ProgressBar 
private lateinit var a:StarAdapter 
private lateinit var b:Button 
private lateinit var c:TextView 
private val l=mutableListOf<Star>() 
private var loading=false 
private var off=0 
private val bs=200000 
private var more=true 
private var vis=false 
private lateinit var db:StarDbHelper 
private var lat=0.0 
private var lon=0.0 
override fun 
onCreate(bd:Bundle?){super.onCreate(bd);setContentView(R.layout.activity_star_cata
log);r=findViewById(R.id.recyclerStars);s=findViewById(R.id.searchBox);e=findViewB
yId(R.id.emptyView);p=findViewById(R.id.loadingSpinner);b=findViewById(R.id.btnFil
terVisibility);c=findViewById(R.id.tvStarCount);a=StarAdapter(l,{st-
>info(st)},{st-
>visible(st,Date())});r.adapter=a;r.layoutManager=LinearLayoutManager(this);val 
ll=BaseActivity.loadLocation(this);lat=ll.first;lon=ll.second;db=StarDbHelper(this
);p.visibility=View.VISIBLE;lifecycleScope.launch(Dispatchers.IO){db.createDatabas
eIfNeeded();withContext(Dispatchers.Main){p.visibility=View.GONE;init()}};updBtn()
;b.setOnClickListener{vis=!vis;updBtn();reload()};search();scroll()} 
private fun 
updBtn(){if(vis){b.text=getString(R.string.filter_visible_only);b.backgroundTintLi
st=ContextCompat.getColorStateList(this,R.color.hot_pink);s.backgroundTintList=Con
textCompat.getColorStateList(this,R.color.hot_pink)}else{b.text=getString(R.string
.filter_all_stars);b.backgroundTintList=ContextCompat.getColorStateList(this,R.col
or.burnt_orange);s.backgroundTintList=ContextCompat.getColorStateList(this,R.color
.burnt_orange)}} 
private fun reload(){val q=s.text.toString();if(q.isNotEmpty())find(q)else init()} 
private fun 
info(st:Star){if(lat==0.0&&lon==0.0){Toast.makeText(this,getString(R.string.no_loc
ation_set_error),Toast.LENGTH_LONG).show();return};val d=Date();val jd=jd(d);val 
g=gmst(jd);val lst=(g+lon/15)%24;val l=if(lst<0)lst+24 else lst;val 
h=floor(l).toInt();val m=floor((l-h)*60).toInt();val 
lstf="%02d:%02d".format(h,m);val rah=st.ra/15;val rh=floor(rah).toInt();val 
rm=floor((rah-rh)*60).toInt();val raf="%02d:%02d".format(rh,rm);val 
(az,alt)=AstroCalculator.calculateHorizontalCoords(lat,l,st.ra/15,st.dec);val 
msg="""${getString(R.string.nav_info_star_name).format(st.name)}${'\n'}${getString
(R.string.star_type).format(st.type)}${'\n'}${getString(R.string.nav_info_coords_e
q).format(raf,String.format("%.4f",st.dec))}${'\n'}${getString(R.string.nav_info_o
bserver).format(String.format("%.4f",lat),String.format("%.4f",lon))}${'\n'}${getS
tring(R.string.nav_info_sidereal_time).format(lstf)}${'\n'}${getString(R.string.na
101 
482.ЧДТУ 252477 12 01 16 
v_info_azimuth).format(String.format("%.2f",az))}${'\n'}${getString(R.string.nav_i
nfo_altitude).format(String.format("%.2f",alt))}""";val 
dgl=AlertDialog.Builder(this).setTitle(R.string.star_nav_title).setMessage(msg).se
tPositiveButton(android.R.string.ok,null).create();dgl.show();dgl.window?.setBackg
roundDrawable(ColorDrawable(ContextCompat.getColor(this,R.color.beige_bg)))} 
private fun visible(st:Star,d:Date):Boolean{if(lat==0.0&&lon==0.0)return false;val 
jd=jd(d);val g=gmst(jd);val l=(g+lon/15)%24;val ln=if(l<0)l+24 else l;val 
(_,alt)=AstroCalculator.calculateHorizontalCoords(lat,ln,st.ra/15,st.dec);return 
alt>0} 
private fun search(){s.addTextChangedListener(object:TextWatcher{override fun 
afterTextChanged(t:Editable?){if(t.isNullOrEmpty())init()else 
find(t.toString())}override fun 
beforeTextChanged(a:CharSequence?,b:Int,c:Int,d:Int){}override fun 
onTextChanged(a:CharSequence?,b:Int,c:Int,d:Int){}})} 
private fun 
scroll(){r.addOnScrollListener(object:RecyclerView.OnScrollListener(){override fun 
onScrolled(rv:RecyclerView,dx:Int,dy:Int){val lm=rv.layoutManager as 
LinearLayoutManager;val v=lm.childCount;val t=lm.itemCount;val 
f=lm.findFirstVisibleItemPosition();if(!loading&&more&&(v+f)>=t&&f>=0)more()}})} 
private fun 
init(){loading=true;p.visibility=View.VISIBLE;off=0;if(vis&&lat==0.0&&lon==0.0){To
ast.makeText(this,getString(R.string.no_location_set_error),Toast.LENGTH_LONG).sho
w();vis=false;updBtn()};lifecycleScope.launch(Dispatchers.IO){val 
i=db.getStarsBatch(off,bs);val r=if(vis)i.filter{visible(it,Date())}else 
i;withContext(Dispatchers.Main){l.clear();l.addAll(r);a.updateData(r);c.text=getSt
ring(R.string.stars_count,l.size);more=i.size==bs;off+=i.size;p.visibility=View.GO
NE;loading=false;e.visibility=if(l.isEmpty())View.VISIBLE else View.GONE}}} 
private fun 
more(){if(loading||!more)return;if(vis&&lat==0.0&&lon==0.0){Toast.makeText(this,ge
tString(R.string.no_location_set_error),Toast.LENGTH_SHORT).show();vis=false;updBt
n();return};loading=true;p.visibility=View.VISIBLE;lifecycleScope.launch(Dispatche
rs.IO){val n=db.getStarsBatch(off,bs);val 
r=if(vis)n.filter{visible(it,Date())}else 
n;withContext(Dispatchers.Main){l.addAll(r);a.addData(r);c.text=getString(R.string
.stars_count,l.size);more=n.size==bs;off+=n.size;p.visibility=View.GONE;loading=fa
lse}}} 
private fun 
find(q:String){loading=true;p.visibility=View.VISIBLE;off=0;if(vis&&lat==0.0&&lon=
=0.0){Toast.makeText(this,getString(R.string.no_location_set_error),Toast.LENGTH_S
HORT).show();vis=false;updBtn()};lifecycleScope.launch(Dispatchers.IO){val 
r=db.searchStars(q);val f=if(vis)r.filter{visible(it,Date())}else 
r;withContext(Dispatchers.Main){l.clear();l.addAll(f);a.updateData(f);c.text=getSt
ring(R.string.stars_count,l.size);more=false;p.visibility=View.GONE;loading=false;
e.visibility=if(l.isEmpty())View.VISIBLE else View.GONE}}} 
private fun jd(d:Date):Double{val 
c=Calendar.getInstance(TimeZone.getTimeZone("UTC"));c.time=d;var 
y=c[Calendar.YEAR];var m=c[Calendar.MONTH]+1;val da=c[Calendar.DAY_OF_MONTH];val 
h=c[Calendar.HOUR_OF_DAY];val mi=c[Calendar.MINUTE];val 
s=c[Calendar.SECOND];if(m<=2){y--;m+=12};val A=floor(y/100.0);val B=2-
A+floor(A/4);val f=(h+mi/60.0+s/3600.0)/24.0;return 
floor(365.25*(y+4716))+floor(30.6001*(m+1))+da+B-1524.5+f} 
private fun gmst(jd:Double):Double{var g=18.697374558+24.06570982441908*(jd-
2451545.0);g%=24.0;if(g<0)g+=24.0;return g}} 
 
TiltView.kt 
package com.example.poa 
import android.animation.ValueAnimator 
import android.content.Context 
import android.graphics.Canvas 
import android.graphics.Color 
import android.graphics.Paint 
102 
482.ЧДТУ 252477 12 01 17 
import android.util.AttributeSet 
import android.view.View 
import android.view.animation.LinearInterpolator 
import androidx.core.graphics.withRotation 
class TiltView(context:Context,attrs:AttributeSet):View(context,attrs){private var 
angle:Float=0f;private val 
paintEarth=Paint().apply{color=Color.BLUE;style=Paint.Style.FILL};private val 
paintEquator=Paint().apply{color=Color.WHITE;strokeWidth=4f;style=Paint.Style.STRO
KE};private val 
paintAxis=Paint().apply{color=Color.RED;strokeWidth=6f;style=Paint.Style.STROKE};f
un 
setTiltAnimated(newAngle:Float){ValueAnimator.ofFloat(angle,newAngle).apply{durati
on=1000;interpolator=LinearInterpolator();addUpdateListener{angle=it.animatedValue 
as Float;invalidate()};start()}}override fun 
onDraw(canvas:Canvas){super.onDraw(canvas);val cx=width/2f;val cy=height/2f;val 
r=width.coerceAtMost(height)/3f;canvas.drawCircle(cx,cy,r,paintEarth);canvas.withR
otation(angle,cx,cy){drawLine(cx,cy-
r,cx,cy+r,paintAxis);drawCircle(cx,cy,r,paintEquator)}}} 
 
TimeSiderealActivity.kt 
package com.example.poa 
import android.Manifest 
import android.content.pm.PackageManager 
import android.location.Location 
import android.os.Bundle 
import android.util.Log 
import android.widget.* 
import androidx.annotation.RequiresPermission 
import androidx.core.app.ActivityCompat 
import androidx.lifecycle.lifecycleScope 
import com.google.android.gms.common.api.ApiException 
import com.google.android.gms.location.LocationServices 
import com.google.android.gms.location.Priority 
import com.google.android.gms.location.LocationSettingsStatusCodes 
import com.google.android.gms.tasks.CancellationTokenSource 
import kotlinx.coroutines.* 
import java.util.* 
import kotlin.coroutines.coroutineContext 
import kotlin.math.* 
class TimeSiderealActivity:BaseActivity(){ 
private lateinit var tvLST:TextView;private lateinit var tvLocal:TextView;private 
lateinit var tvUTC:TextView;private lateinit var tvPeriod:TextView;private 
lateinit var dayProgress:ProgressBar;private lateinit var 
btnSetLocation:Button;private lateinit var earthView:EarthGLSurfaceView 
private var longitude=0.0;private var latitude=0.0 
private val locationClient by 
lazy{LocationServices.getFusedLocationProviderClient(this)} 
private val cancellationTokenSource=CancellationTokenSource() 
companion object{private const val LOCATION_PERMISSION_REQUEST=101} 
override fun 
onCreate(savedInstanceState:Bundle?){super.onCreate(savedInstanceState);setContent
View(R.layout.activity_time_sidereal) 
tvLST=findViewById(R.id.tv_lst);tvLocal=findViewById(R.id.tv_local);tvUTC=findView
ById(R.id.tv_utc);tvPeriod=findViewById(R.id.tv_period);dayProgress=findViewById(R
.id.day_progress);btnSetLocation=findViewById(R.id.btn_set_location);earthView=fin
dViewById(R.id.earth_view) 
earthView.setTiltAngle(23.44f) 
val(savedLat,savedLon)=BaseActivity.loadLocation(this);latitude=savedLat;longitude
=savedLon;updateLocationUI() 
btnSetLocation.setOnClickListener{checkLocationPermission()} 
lifecycleScope.launch{timeUpdateLoop()}} 
103 
482.ЧДТУ 252477 12 01 18 
override fun onStop(){super.onStop();cancellationTokenSource.cancel()} 
private fun 
checkLocationPermission(){if(ActivityCompat.checkSelfPermission(this,Manifest.perm
ission.ACCESS_FINE_LOCATION)!=PackageManager.PERMISSION_GRANTED){ActivityCompat.re
questPermissions(this,arrayOf(Manifest.permission.ACCESS_FINE_LOCATION),LOCATION_P
ERMISSION_REQUEST)}else fetchCurrentLocation()} 
@RequiresPermission(allOf=[Manifest.permission.ACCESS_FINE_LOCATION,Manifest.permi
ssion.ACCESS_COARSE_LOCATION]) 
override fun onRequestPermissionsResult(requestCode:Int,permissions:Array<out 
String>,grantResults:IntArray){super.onRequestPermissionsResult(requestCode,permis
sions,grantResults) 
if(requestCode==LOCATION_PERMISSION_REQUEST&&grantResults.isNotEmpty()&&grantResul
ts[0]==PackageManager.PERMISSION_GRANTED)fetchCurrentLocation()else 
Toast.makeText(this,getString(R.string.location_permission_denied),Toast.LENGTH_SH
ORT).show()} 
@RequiresPermission(allOf=[Manifest.permission.ACCESS_FINE_LOCATION,Manifest.permi
ssion.ACCESS_COARSE_LOCATION]) 
private fun 
fetchCurrentLocation(){locationClient.getCurrentLocation(Priority.PRIORITY_HIGH_AC
CURACY,cancellationTokenSource.token).addOnSuccessListener{location:Location?-> 
if(location!=null){latitude=location.latitude;longitude=location.longitude;saveLoc
ation(this,latitude,longitude);updateLocationUI();Toast.makeText(this,getString(R.
string.location_updated_success),Toast.LENGTH_SHORT).show()}else 
Toast.makeText(this,getString(R.string.location_not_found),Toast.LENGTH_SHORT).sho
w()}.addOnFailureListener{e-> 
val error=when((e as? 
ApiException)?.statusCode){LocationSettingsStatusCodes.RESOLUTION_REQUIRED-
>getString(R.string.gps_disabled_error)else-
>getString(R.string.location_fetch_failed)} 
Toast.makeText(this,error,Toast.LENGTH_LONG).show();Log.e("TimeSiderealActivity","
GPS error",e)}} 
private fun updateLocationUI(){earthView.setMarker(latitude,longitude) 
val jd=julianDate(Date());val gmst=gmstHours(jd);val 
lst=(gmst+longitude/15.0)%24.0 
tvPeriod.text=""" 
${getString(R.string.label_latitude).format(String.format("%.4f",latitude))} 
${getString(R.string.label_longitude).format(String.format("%.4f",longitude))} 
GMST: ${String.format("%.4f",gmst)} год. 
LST:  ${String.format("%.4f",lst)} год. 
JD:   ${String.format("%.4f",jd)} 
""".trimIndent()} 
private suspend fun 
timeUpdateLoop(){while(coroutineContext.isActive){updateTimeDisplay(Date());delay(
1000)}}private fun updateTimeDisplay(now:Date){val 
locale=BaseActivity.getLocale(this) 
val calLocal=Calendar.getInstance();val 
calUTC=Calendar.getInstance(TimeZone.getTimeZone("UTC")) 
tvLocal.text=getString(R.string.time_local).format(String.format(locale,"%02d:%02d
:%02d, %d %tB 
%tY",calLocal[Calendar.HOUR_OF_DAY],calLocal[Calendar.MINUTE],calLocal[Calendar.SE
COND],calLocal[Calendar.DAY_OF_MONTH],calLocal,calLocal)) 
tvUTC.text=getString(R.string.time_utc).format(String.format(locale,"%02d:%02d:%02
d, %d %tB 
%tY",calUTC[Calendar.HOUR_OF_DAY],calUTC[Calendar.MINUTE],calUTC[Calendar.SECOND],
calUTC[Calendar.DAY_OF_MONTH],calUTC,calUTC)) 
val jd=julianDate(now);val gmst=gmstHours(jd);val lst=(gmst+longitude/15.0)%24.0 
tvLST.text=getString(R.string.time_lst).format(toHMS(lst))val 
secToday=calLocal[Calendar.HOUR_OF_DAY]*3600+calLocal[Calendar.MINUTE]*60+calLocal
[Calendar.SECOND] 
dayProgress.progress=(secToday*1000/86400f).toInt()} 
private fun julianDate(date:Date):Double{val 
cal=Calendar.getInstance(TimeZone.getTimeZone("UTC"));cal.time=date 
104 
482.ЧДТУ 252477 12 01 19 
var Y=cal[Calendar.YEAR];var M=cal[Calendar.MONTH]+1;val 
D=cal[Calendar.DAY_OF_MONTH];val H=cal[Calendar.HOUR_OF_DAY];val 
Min=cal[Calendar.MINUTE];val S=cal[Calendar.SECOND] 
if(M<=2){Y--;M+=12} 
val A=floor(Y/100.0);val B=2-A+floor(A/4);val frac=(H+Min/60.0+S/3600.0)/24.0 
return floor(365.25*(Y+4716))+floor(30.6001*(M+1))+D+B-1524.5+frac} 
private fun gmstHours(jd:Double):Double{val T=jd-2451545.0;var 
gmst=18.697374558+24.06570982441908*T;gmst%=24.0;if(gmst<0)gmst+=24.0;return gmst} 
private fun toHMS(h:Double):String{val H=floor(h).toInt();val M=floor((h-
H)*60).toInt();val S=floor((h-H-
M/60.0)*3600).toInt();return"%02d:%02d:%02d".format(H,M,S)}} 
activity_accelerometer.xml 
<?xml version="1.0" encoding="utf-8"?><androidx.drawerlayout.widget.DrawerLayout 
xmlns:android="http://schemas.android.com/apk/res/android" 
xmlns:app="http://schemas.android.com/apk/res-auto" 
android:id="@+id/drawer_layout" android:fitsSystemWindows="true" 
android:layout_width="match_parent" android:layout_height="match_parent" 
android:background="@color/beige_bg"><LinearLayout android:orientation="vertical" 
android:layout_width="match_parent" 
android:layout_height="match_parent"><androidx.appcompat.widget.Toolbar 
android:id="@+id/toolbar" android:layout_width="match_parent" 
android:layout_height="?attr/actionBarSize" 
android:background="?attr/colorPrimary" app:title="@string/accelerometer" 
app:titleTextColor="@color/white"/><ScrollView android:layout_width="match_parent" 
android:layout_height="match_parent"><LinearLayout android:orientation="vertical" 
android:gravity="center_horizontal" android:padding="24dp" 
android:layout_width="match_parent" android:layout_height="wrap_content"><TextView 
android:id="@+id/textExtraInfo" android:layout_width="wrap_content" 
android:layout_height="wrap_content" android:textSize="18sp" 
android:textColor="?attr/colorPrimary" android:paddingTop="24dp" 
android:layout_gravity="center_horizontal" android:text=""/><TextView 
android:id="@+id/textAccX" android:layout_width="wrap_content" 
android:layout_height="wrap_content" android:text="X: 0.00" 
android:textSize="24sp" android:padding="12dp" 
android:textColor="?attr/colorPrimary"/><TextView android:id="@+id/textAccY" 
android:layout_width="wrap_content" android:layout_height="wrap_content" 
android:text="Y: 0.00" android:textSize="24sp" android:padding="12dp" 
android:textColor="?attr/colorPrimary"/><TextView android:id="@+id/textAccZ" 
android:layout_width="wrap_content" android:layout_height="wrap_content" 
android:text="Z: 0.00" android:textSize="24sp" android:padding="12dp" 
android:textColor="?attr/colorPrimary"/><LinearLayout 
android:layout_width="wrap_content" android:layout_height="wrap_content" 
android:orientation="horizontal" android:gravity="center_vertical" 
android:paddingTop="24dp" android:paddingLeft="8dp" 
android:paddingRight="8dp"><TextView android:id="@+id/textPhoneState" 
android:layout_width="wrap_content" android:layout_height="wrap_content" 
android:textSize="22sp" android:textStyle="bold" 
android:textColor="?attr/colorPrimary" android:text="@string/state_label"/><View 
android:id="@+id/stateIndicator" android:layout_width="20dp" 
android:layout_height="20dp" android:layout_marginStart="12dp" 
android:background="@drawable/circle_indicator"/></LinearLayout></LinearLayout></S
crollView></LinearLayout><com.google.android.material.navigation.NavigationView 
android:id="@+id/nav_view" android:layout_width="wrap_content" 
android:layout_height="match_parent" android:layout_gravity="start" 
android:fitsSystemWindows="true" android:background="@color/forest_green" 
app:itemTextColor="@android:color/white" app:itemIconTint="@android:color/white" 
app:subheaderTextAppearance="@style/WhiteSubMenuTitle" 
app:menu="@menu/drawer_menu" 
app:itemIconSize="32dp"/></androidx.drawerlayout.widget.DrawerLayout> 
activity_gyro.xml 
105 
482.ЧДТУ 252477 12 01 20 
<?xml version="1.0" encoding="utf-8"?><androidx.drawerlayout.widget.DrawerLayout 
xmlns:android="http://schemas.android.com/apk/res/android" 
xmlns:app="http://schemas.android.com/apk/res-auto" 
android:id="@+id/drawer_layout" android:fitsSystemWindows="true" 
android:layout_width="match_parent" android:layout_height="match_parent" 
android:background="@color/beige_bg"><LinearLayout android:orientation="vertical" 
android:layout_width="match_parent" 
android:layout_height="match_parent"><androidx.appcompat.widget.Toolbar 
android:id="@+id/toolbar" android:layout_width="match_parent" 
android:layout_height="?attr/actionBarSize" 
android:background="?attr/colorPrimary" app:title="@string/gyroscope" 
app:titleTextColor="@android:color/white"/><ScrollView 
android:layout_width="match_parent" 
android:layout_height="match_parent"><LinearLayout android:orientation="vertical" 
android:gravity="center_horizontal" android:padding="24dp" 
android:layout_width="match_parent" android:layout_height="wrap_content"><TextView 
android:id="@+id/textExtraInfo" android:textSize="20sp" android:textColor="#444" 
android:paddingTop="24dp" android:layout_width="wrap_content" 
android:layout_height="wrap_content"/><TextView android:id="@+id/textGyroX" 
android:text="X: 0.00" android:textSize="24sp" android:padding="12dp" 
android:textColor="?attr/colorPrimary" android:layout_width="wrap_content" 
android:layout_height="wrap_content"/><TextView android:id="@+id/textGyroY" 
android:text="Y: 0.00" android:textSize="24sp" android:padding="12dp" 
android:textColor="?attr/colorPrimary" android:layout_width="wrap_content" 
android:layout_height="wrap_content"/><TextView android:id="@+id/textGyroZ" 
android:text="Z: 0.00" android:textSize="24sp" android:padding="12dp" 
android:textColor="?attr/colorPrimary" android:layout_width="wrap_content" 
android:layout_height="wrap_content"/><TextView 
android:id="@+id/textRotationState" android:text="@string/rotation_label" 
android:textStyle="bold" android:textSize="20sp" android:paddingTop="24dp" 
android:gravity="center_horizontal" android:textColor="?attr/colorPrimary" 
android:layout_width="wrap_content" 
android:layout_height="wrap_content"/></LinearLayout></ScrollView></LinearLayout><
com.google.android.material.navigation.NavigationView android:id="@+id/nav_view" 
android:layout_width="wrap_content" android:layout_height="match_parent" 
android:layout_gravity="start" android:fitsSystemWindows="true" 
android:background="@color/forest_green" app:itemTextColor="@android:color/white" 
app:subheaderTextAppearance="@style/WhiteSubMenuTitle" 
app:itemIconTint="@android:color/white" app:menu="@menu/drawer_menu" 
app:itemIconSize="32dp"/></androidx.drawerlayout.widget.DrawerLayout> 
activity_magnetometer.xml 
<?xml version="1.0" encoding="utf-8"?><androidx.drawerlayout.widget.DrawerLayout 
xmlns:android="http://schemas.android.com/apk/res/android" 
xmlns:app="http://schemas.android.com/apk/res-auto" 
android:id="@+id/drawer_layout" android:layout_width="match_parent" 
android:layout_height="match_parent" android:fitsSystemWindows="true" 
android:background="@color/beige_bg"><LinearLayout android:orientation="vertical" 
android:layout_width="match_parent" 
android:layout_height="match_parent"><androidx.appcompat.widget.Toolbar 
android:id="@+id/toolbar" android:layout_width="match_parent" 
android:layout_height="?attr/actionBarSize" 
android:background="?attr/colorPrimary" app:title="@string/magnetometer" 
app:titleTextColor="@android:color/white"/><ScrollView 
android:layout_width="match_parent" 
android:layout_height="match_parent"><LinearLayout android:orientation="vertical" 
android:gravity="center_horizontal" android:padding="24dp" 
android:layout_width="match_parent" android:layout_height="wrap_content"><TextView 
android:id="@+id/textExtraInfo" android:textSize="20sp" android:paddingTop="24dp" 
android:textColor="?attr/colorPrimary" android:layout_width="wrap_content" 
android:layout_height="wrap_content"/><TextView android:id="@+id/textMagX" 
android:text="X: 0.00" android:textSize="24sp" android:padding="12dp" 
106 
482.ЧДТУ 252477 12 01 21 
android:textColor="?attr/colorPrimary" android:layout_width="wrap_content" 
android:layout_height="wrap_content"/><TextView android:id="@+id/textMagY" 
android:text="Y: 0.00" android:textSize="24sp" android:padding="12dp" 
android:textColor="?attr/colorPrimary" android:layout_width="wrap_content" 
android:layout_height="wrap_content"/><TextView android:id="@+id/textMagZ" 
android:text="Z: 0.00" android:textSize="24sp" android:padding="12dp" 
android:textColor="?attr/colorPrimary" android:layout_width="wrap_content" 
android:layout_height="wrap_content"/><TextView android:id="@+id/textCompassState" 
android:text="@string/compass_info" android:textStyle="bold" 
android:textSize="22sp" android:paddingTop="24dp" 
android:gravity="center_horizontal" android:textColor="?attr/colorPrimary" 
android:layout_width="wrap_content" 
android:layout_height="wrap_content"/></LinearLayout></ScrollView></LinearLayout><
com.google.android.material.navigation.NavigationView android:id="@+id/nav_view" 
android:layout_width="wrap_content" android:layout_height="match_parent" 
android:layout_gravity="start" android:fitsSystemWindows="true" 
android:background="@color/forest_green" app:itemTextColor="@android:color/white" 
app:subheaderTextAppearance="@style/WhiteSubMenuTitle" 
app:itemIconTint="@android:color/white" app:menu="@menu/drawer_menu" 
app:itemIconSize="32dp"/></androidx.drawerlayout.widget.DrawerLayout> 
activity_main.xml 
<?xml version="1.0" encoding="utf-8"?><androidx.drawerlayout.widget.DrawerLayout 
xmlns:android="http://schemas.android.com/apk/res/android" 
xmlns:app="http://schemas.android.com/apk/res-auto" 
android:id="@+id/drawer_layout" android:layout_width="match_parent" 
android:layout_height="match_parent" 
android:fitsSystemWindows="true"><LinearLayout android:layout_width="match_parent" 
android:layout_height="match_parent" android:orientation="vertical" 
android:background="@color/beige_bg"><androidx.appcompat.widget.Toolbar 
android:id="@+id/toolbar" android:layout_width="match_parent" 
android:layout_height="?attr/actionBarSize" 
android:background="@color/forest_green" app:title="@string/app_name" 
app:titleTextColor="@android:color/white"/><FrameLayout 
android:layout_width="match_parent" android:layout_height="0dp" 
android:layout_weight="1" android:background="@color/beige_bg"><TextureView 
android:id="@+id/cameraPreview" android:layout_width="match_parent" 
android:layout_height="match_parent"/><TextView android:id="@+id/cameraText" 
android:layout_width="match_parent" android:layout_height="wrap_content" 
android:layout_gravity="center" android:text="@string/info_text" 
android:textColor="@color/forest_green" android:textAlignment="center" 
android:textSize="24sp" android:textStyle="bold"/><View 
android:id="@+id/flashView" android:layout_width="match_parent" 
android:layout_height="match_parent" android:background="#FFFFFF" 
android:alpha="0"/><LinearLayout android:layout_width="match_parent" 
android:layout_height="wrap_content" android:layout_gravity="bottom" 
android:orientation="horizontal" android:gravity="center_vertical" 
android:paddingStart="16dp" android:paddingEnd="16dp" 
android:paddingBottom="32dp"><Button android:id="@+id/button" 
android:layout_width="0dp" android:layout_height="wrap_content" 
android:layout_weight="1" android:layout_marginStart="16dp" 
android:layout_marginEnd="16dp" android:backgroundTint="@color/hot_pink" 
android:text="@string/shoot" android:textColor="@android:color/white" 
android:textSize="18sp" 
android:minHeight="64dp"/></LinearLayout></FrameLayout></LinearLayout><com.google.
android.material.navigation.NavigationView android:id="@+id/nav_view" 
android:layout_width="280dp" android:layout_height="match_parent" 
android:layout_gravity="start" android:background="@color/forest_green" 
app:itemTextColor="@android:color/white" app:itemIconTint="@android:color/white" 
app:itemIconSize="32dp" 
app:menu="@menu/drawer_menu"/></androidx.drawerlayout.widget.DrawerLayout> 
107 
482.ЧДТУ 252477 12 01 22 
activity_polaris_finder.xml 
<?xml version="1.0" encoding="utf-8"?><androidx.drawerlayout.widget.DrawerLayout 
xmlns:android="http://schemas.android.com/apk/res/android" 
xmlns:app="http://schemas.android.com/apk/res-auto" 
android:id="@+id/drawer_layout" android:layout_width="match_parent" 
android:layout_height="match_parent" android:fitsSystemWindows="true" 
android:background="@drawable/bg_color"><androidx.constraintlayout.widget.Constrai
ntLayout android:layout_width="match_parent" android:layout_height="match_parent" 
android:background="@drawable/bg_color"><androidx.appcompat.widget.Toolbar 
android:id="@+id/toolbar" android:layout_width="0dp" 
android:layout_height="?attr/actionBarSize" 
android:background="?attr/colorPrimary" app:title="@string/title_polaris_finder" 
app:titleTextColor="@android:color/white" 
app:layout_constraintTop_toTopOf="parent" 
app:layout_constraintStart_toStartOf="parent" 
app:layout_constraintEnd_toEndOf="parent"/><ImageView android:id="@+id/glowEffect" 
android:layout_width="260dp" android:layout_height="260dp" 
android:src="@drawable/glow" android:alpha="0" android:layout_marginTop="40dp" 
app:layout_constraintTop_toBottomOf="@id/toolbar" 
app:layout_constraintStart_toStartOf="parent" 
app:layout_constraintEnd_toEndOf="parent"/><ImageView android:id="@+id/arrowView" 
android:layout_width="180dp" android:layout_height="180dp" 
android:src="@drawable/ic_arrow_white" android:layout_marginTop="70dp" 
app:layout_constraintTop_toBottomOf="@id/toolbar" 
app:layout_constraintStart_toStartOf="parent" 
app:layout_constraintEnd_toEndOf="parent"/><TextView android:id="@+id/angleText" 
android:layout_width="wrap_content" android:layout_height="wrap_content" 
android:text="@string/_0" android:textSize="24sp" android:textStyle="bold" 
android:textColor="?attr/colorPrimary" android:layout_marginTop="40dp" 
app:layout_constraintTop_toBottomOf="@id/arrowView" 
app:layout_constraintStart_toStartOf="parent" 
app:layout_constraintEnd_toEndOf="parent"/></androidx.constraintlayout.widget.Cons
traintLayout><com.google.android.material.navigation.NavigationView 
android:id="@+id/nav_view" android:layout_width="wrap_content" 
android:layout_height="match_parent" android:layout_gravity="start" 
android:fitsSystemWindows="true" android:background="@color/forest_green" 
app:itemTextColor="@android:color/white" 
app:subheaderTextAppearance="@style/WhiteSubMenuTitle" 
app:itemIconTint="@android:color/white" app:itemIconSize="32dp" 
app:menu="@menu/drawer_menu"/></androidx.drawerlayout.widget.DrawerLayout> 
activity_star_catalog.xml 
<?xml version="1.0" encoding="utf-8"?><androidx.drawerlayout.widget.DrawerLayout 
xmlns:android="http://schemas.android.com/apk/res/android" 
xmlns:app="http://schemas.android.com/apk/res-auto" 
android:id="@+id/drawer_layout" android:layout_width="match_parent" 
android:layout_height="match_parent" android:background="@color/beige_bg" 
android:fitsSystemWindows="true"><androidx.constraintlayout.widget.ConstraintLayou
t android:layout_width="match_parent" 
android:layout_height="match_parent"><androidx.appcompat.widget.Toolbar 
android:id="@+id/toolbar" android:layout_width="match_parent" 
android:layout_height="?attr/actionBarSize" 
android:background="@color/forest_green" android:elevation="4dp" 
app:title="@string/information_about_stars" 
app:titleTextColor="@android:color/white" 
app:layout_constraintTop_toTopOf="parent" 
app:layout_constraintStart_toStartOf="parent" 
app:layout_constraintEnd_toEndOf="parent"/><androidx.constraintlayout.widget.Const
raintLayout android:id="@+id/searchRow" android:layout_width="0dp" 
android:layout_height="wrap_content" android:layout_marginStart="16dp" 
android:layout_marginEnd="16dp" android:layout_marginTop="8dp" 
108 
482.ЧДТУ 252477 12 01 23 
app:layout_constraintTop_toBottomOf="@id/toolbar" 
app:layout_constraintStart_toStartOf="parent" 
app:layout_constraintEnd_toEndOf="parent"><EditText android:id="@+id/searchBox" 
android:layout_width="0dp" android:layout_height="48dp" android:padding="12dp" 
android:hint="@string/search_star_by_name_or_id" 
android:background="@drawable/edittext_background" 
android:textColor="@color/white" android:textColorHint="@color/white" 
android:backgroundTint="@color/burnt_orange" 
app:layout_constraintStart_toStartOf="parent" 
app:layout_constraintEnd_toStartOf="@+id/btnFilterVisibility" 
app:layout_constraintTop_toTopOf="parent" 
app:layout_constraintBottom_toBottomOf="parent" 
app:layout_constraintHorizontal_weight="3"/><Button 
android:id="@+id/btnFilterVisibility" android:layout_width="0dp" 
android:layout_height="58dp" android:layout_marginStart="12dp" 
android:textSize="12sp" android:text="@string/filter_all_stars" 
android:textColor="@color/white" android:backgroundTint="@color/burnt_orange" 
app:layout_constraintStart_toEndOf="@id/searchBox" 
app:layout_constraintEnd_toEndOf="parent" 
app:layout_constraintTop_toTopOf="@id/searchBox" 
app:layout_constraintBottom_toBottomOf="@id/searchBox" 
app:layout_constraintHorizontal_weight="1"/></androidx.constraintlayout.widget.Con
straintLayout><FrameLayout android:id="@+id/recyclerContainer" 
android:layout_width="0dp" android:layout_height="0dp" 
app:layout_constraintTop_toBottomOf="@id/searchRow" 
app:layout_constraintStart_toStartOf="parent" 
app:layout_constraintEnd_toEndOf="parent" 
app:layout_constraintBottom_toBottomOf="parent"><TextView 
android:id="@+id/tvStarCount" android:layout_width="wrap_content" 
android:layout_height="wrap_content" android:layout_margin="8dp" 
android:text="@string/stars_count" 
android:textSize="16sp"/><androidx.recyclerview.widget.RecyclerView 
android:id="@+id/recyclerStars" android:layout_width="wrap_content" 
android:layout_height="wrap_content" android:layout_gravity="center"/><ProgressBar 
android:id="@+id/loadingSpinner" android:layout_width="60dp" 
android:layout_height="60dp" android:layout_gravity="center" 
android:indeterminateTint="@color/forest_green" 
android:visibility="gone"/><TextView android:id="@+id/emptyView" 
android:layout_width="wrap_content" android:layout_height="wrap_content" 
android:layout_gravity="center" android:text="@string/no_stars_found" 
android:textColor="@color/forest_green" android:textSize="16sp" 
android:visibility="gone"/></FrameLayout></androidx.constraintlayout.widget.Constr
aintLayout><com.google.android.material.navigation.NavigationView 
android:id="@+id/nav_view" android:layout_width="280dp" 
android:layout_height="match_parent" android:layout_gravity="start" 
android:fitsSystemWindows="true" android:background="@color/forest_green" 
app:itemTextColor="@android:color/white" 
app:subheaderTextAppearance="@style/WhiteSubMenuTitle" 
app:itemIconTint="@android:color/white" app:menu="@menu/drawer_menu" 
app:itemIconSize="32dp"/></androidx.drawerlayout.widget.DrawerLayout> 
activity_time_sidereal.xml 
<?xml version="1.0" encoding="utf-8"?><androidx.drawerlayout.widget.DrawerLayout 
xmlns:android="http://schemas.android.com/apk/res/android" 
xmlns:app="http://schemas.android.com/apk/res-auto" 
android:id="@+id/drawer_layout" android:layout_width="match_parent" 
android:layout_height="match_parent" android:background="@color/beige_bg" 
android:fitsSystemWindows="true"><LinearLayout android:orientation="vertical" 
android:layout_width="match_parent" 
android:layout_height="match_parent"><androidx.appcompat.widget.Toolbar 
android:id="@+id/toolbar" android:layout_width="match_parent" 
android:layout_height="?attr/actionBarSize" 
109 
482.ЧДТУ 252477 12 01 24 
android:background="@color/forest_green" android:elevation="4dp" 
app:title="@string/title_sidereal_time" 
app:titleTextColor="@android:color/white"/><androidx.constraintlayout.widget.Const
raintLayout android:layout_width="match_parent" 
android:layout_height="match_parent" android:padding="12dp"><TextView 
android:id="@+id/tv_local" android:layout_width="0dp" 
android:layout_height="wrap_content" android:text="@string/get_location" 
android:textSize="16sp" app:layout_constraintTop_toTopOf="parent" 
app:layout_constraintStart_toStartOf="parent" 
app:layout_constraintEnd_toEndOf="parent"/><ProgressBar 
android:id="@+id/day_progress" style="?android:attr/progressBarStyleHorizontal" 
android:layout_width="0dp" android:layout_height="12dp" android:max="1000" 
android:progress="0" android:layout_marginTop="8dp" 
app:layout_constraintTop_toBottomOf="@id/tv_local" 
app:layout_constraintStart_toStartOf="parent" 
app:layout_constraintEnd_toEndOf="parent"/><TextView android:id="@+id/tv_utc" 
android:layout_width="0dp" android:layout_height="wrap_content" 
android:text="@string/time_utc_default" android:textSize="16sp" 
android:layout_marginTop="8dp" 
app:layout_constraintTop_toBottomOf="@id/day_progress" 
app:layout_constraintStart_toStartOf="parent" 
app:layout_constraintEnd_toEndOf="parent"/><TextView android:id="@+id/tv_lst" 
android:layout_width="0dp" android:layout_height="wrap_content" 
android:text="@string/time_lst_default" android:textSize="20sp" 
android:textStyle="bold" android:layout_marginTop="8dp" 
app:layout_constraintTop_toBottomOf="@id/tv_utc" 
app:layout_constraintStart_toStartOf="parent" 
app:layout_constraintEnd_toEndOf="parent"/><TextView android:id="@+id/tv_period" 
android:layout_width="0dp" android:layout_height="wrap_content" 
android:text="@string/time_period_info" android:textSize="16sp" 
android:layout_marginTop="6dp" app:layout_constraintTop_toBottomOf="@id/tv_lst" 
app:layout_constraintStart_toStartOf="parent" 
app:layout_constraintEnd_toEndOf="parent"/><com.example.poa.TiltView 
android:id="@+id/tilt_view" android:layout_width="0dp" android:layout_height="0dp" 
android:layout_marginTop="8dp" android:layout_marginBottom="8dp" 
app:layout_constraintTop_toBottomOf="@id/tv_period" 
app:layout_constraintBottom_toTopOf="@id/btn_set_location" 
app:layout_constraintStart_toStartOf="parent" 
app:layout_constraintEnd_toEndOf="parent"/><com.example.poa.EarthGLSurfaceView 
android:id="@+id/earth_view" android:layout_width="match_parent" 
android:layout_height="0dp" android:hardwareAccelerated="true" 
app:layout_constraintTop_toBottomOf="@id/tv_period" 
app:layout_constraintBottom_toTopOf="@id/btn_set_location" 
app:layout_constraintStart_toStartOf="parent" 
app:layout_constraintEnd_toEndOf="parent"/><Button 
android:id="@+id/btn_set_location" android:layout_width="wrap_content" 
android:layout_height="wrap_content" android:text="@string/get_location" 
android:layout_marginBottom="24dp" app:layout_constraintBottom_toBottomOf="parent" 
app:layout_constraintStart_toStartOf="parent" 
app:layout_constraintEnd_toEndOf="parent"/></androidx.constraintlayout.widget.Cons
traintLayout></LinearLayout><com.google.android.material.navigation.NavigationView 
android:id="@+id/nav_view" android:layout_width="280dp" 
android:layout_height="match_parent" android:layout_gravity="start" 
android:background="@color/forest_green" android:fitsSystemWindows="true" 
app:menu="@menu/drawer_menu"app:subheaderTextAppearance="@style/WhiteSubMenuTitle" 
app:itemTextColor="@android:color/white"app:itemIconTint="@android:color/white"/><
/androidx.drawerlayout.widget.DrawerLayout> 
dialog_settings.xml 
<?xml version="1.0" encoding="utf-8"?><LinearLayout 
xmlns:android="http://schemas.android.com/apk/res/android" 
android:layout_width="match_parent" android:layout_height="wrap_content" 
110 
482.ЧДТУ 252477 12 01 25 
android:orientation="vertical" android:padding="16dp"><TextView 
android:layout_width="match_parent" android:layout_height="wrap_content" 
android:text="@string/interval_label" android:textSize="16sp"/><EditText 
android:id="@+id/intervalInput" android:layout_width="match_parent" 
android:layout_height="wrap_content" android:autofillHints="" 
android:inputType="number" android:hint="@string/interval_hint"/><TextView 
android:layout_width="match_parent" android:layout_height="wrap_content" 
android:text="@string/max_photos_label" android:textSize="16sp" 
android:layout_marginTop="8dp"/><EditText android:id="@+id/maxPhotosInput" 
android:layout_width="match_parent" android:layout_height="wrap_content" 
android:autofillHints="" android:inputType="number" 
android:hint="@string/max_photos_hint"/></LinearLayout> 
item_star.xml 
<?xml version="1.0" encoding="utf-8"?><LinearLayout 
xmlns:android="http://schemas.android.com/apk/res/android" 
android:layout_width="match_parent" android:layout_height="wrap_content" 
android:orientation="vertical" android:padding="12dp" 
android:fitsSystemWindows="true" android:background="@color/beige_bg"><TextView 
android:id="@+id/starName" android:layout_width="match_parent" 
android:layout_height="wrap_content" android:textSize="16sp" 
android:textColor="@color/burnt_orange" android:textStyle="bold"/><TextView 
android:id="@+id/starCoord" android:layout_width="match_parent" 
android:layout_height="wrap_content" android:textColor="@color/burnt_orange" 
android:textSize="14sp"/><TextView android:id="@+id/starType" 
android:layout_width="match_parent" android:layout_height="wrap_content" 
android:textColor="@color/burnt_orange" android:textSize="12sp"/></LinearLayout> 
drawer_menu.xml 
<?xml version="1.0" encoding="utf-8"?><menu 
xmlns:android="http://schemas.android.com/apk/res/android"><group 
android:checkableBehavior="single"><item android:id="@+id/nav_main" 
android:title="@string/main_activity" android:icon="@drawable/ic_home"/><item 
android:id="@+id/nav_info" android:title="@string/information_about_stars" 
android:icon="@android:drawable/ic_dialog_info"/><item android:id="@+id/nav_accel" 
android:title="@string/accelerometer"android:icon="@drawable/ic_accelerometer"/><i
tem android:id="@+id/nav_gyroscope" android:title="@string/gyroscope" 
android:icon="@drawable/ic_compass"/><item android:id="@+id/nav_magnet" 
android:title="@string/magnetometer"android:icon="@android:drawable/ic_menu_myloca
tion"/><item android:id="@+id/nav_polaris" android:title="@string/polaris_finder" 
android:icon="@drawable/ic_compass"/><item android:id="@+id/nav_time_sidereal" 
android:title="@string/real_time" android:icon="@drawable/ic_clock"/></group><item 
android:title="@string/title_other"><menu><group android:checkableBehavior="none" 
android:titleTextAppearance="@style/WhiteSubMenuTitle"><item 
android:id="@+id/nav_language" android:title="@string/change_language" 
android:icon="@drawable/ic_language"/><item android:id="@+id/nav_website" 
android:title="@string/link"android:icon="@drawable/ic_smile"/></group></menu></it
em></menu> 
AndroidManifest.xml 
<?xml version="1.0" encoding="utf-8"?><manifest 
xmlns:tools="http://schemas.android.com/tools" 
xmlns:android="http://schemas.android.com/apk/res/android" 
tools:ignore="MissingLeanbackLauncher"><uses-permission 
android:name="android.permission.HIGH_SAMPLING_RATE_SENSORS"/><uses-permission 
android:name="android.permission.ACCESS_FINE_LOCATION"/><uses-permission 
android:name="android.permission.ACCESS_COARSE_LOCATION"/><uses-permission 
android:name="android.permission.CAMERA"/><uses-feature 
android:name="android.software.leanback" android:required="false"/><uses-feature 
android:name="android.hardware.touchscreen" android:required="false"/><uses-
111 
482.ЧДТУ 252477 12 01 26 
feature android:name="android.hardware.sensor.accelerometer" 
android:required="true"/><uses-feature 
android:name="android.hardware.sensor.compass" android:required="true"/><uses-
feature android:name="android.hardware.sensor.gyroscope" 
android:required="true"/><uses-feature android:name="android.hardware.camera" 
android:required="false"/><uses-feature 
android:name="android.hardware.location.gps" android:required="false"/><uses-
permission android:name="android.permission.WAKE_LOCK"/><application 
android:allowBackup="true" 
android:dataExtractionRules="@xml/data_extraction_rules" 
android:fullBackupContent="@xml/backup_rules" android:icon="@mipmap/ic_launcher" 
android:label="@string/app_name" android:roundIcon="@drawable/stars" 
android:supportsRtl="true" android:theme="@style/Theme.POA"><activity 
android:name=".MainActivity" android:exported="true" 
android:theme="@style/Theme.POA"><intent-filter><action 
android:name="android.intent.action.MAIN"/><category 
android:name="android.intent.category.LAUNCHER"/></intent-
filter></activity><activity android:name=".StarCatalogActivity" 
android:theme="@style/Theme.POA"/><activity android:name=".GyroscopeActivity" 
android:theme="@style/Theme.POA"/><activity android:name=".AccelerometerActivity" 
android:theme="@style/Theme.POA"/><activity android:name=".MagnetometerActivity" 
android:theme="@style/Theme.POA"/><activity android:name=".PolarisFinderActivity" 
android:exported="false" android:theme="@style/Theme.POA"/><activity 
android:name=".TimeSiderealActivity" android:theme="@style/Theme.POA" 
android:exported="false"/></application></manifest> 
 
 
 
 
112 
ДОДАТОК В 
 
 
 
 
Програмна платформа комплексу оптичної астронавігації 
 
 
 
 
 
 
 
Графічні матеріали 
482.ЧДТУ 252477 90 01 
Листів 12 
 
 
 
Розробник                  ________________     Веретільник В.О. 
 
 
 
 
 
 
 
 
2025 
 
 
482.ЧДТУ 252477 90 01 2 
 
Рисунок В.1 – Титулка 
 
Рисунок В.2 – Мета 
 
Рисунок В.3 – Мета передбачає 
114 
482.ЧДТУ 252477 90 01 3 
 
Рисунок В.4 – Завдання роботи 
 
Рисунок В.5 – Гіпотеза 
 
Рисунок В.6 – Теорія 
115 
482.ЧДТУ 252477 90 01 4 
 
Рисунок В.7 – Експеремент №1 
 
Рисунок В.8 – Експеремент №2 
 
Рисунок В.9 – Експеремент №3 
116 
482.ЧДТУ 252477 90 01 5 
 
Рисунок В.10 – Експеремент №4 
 
Рисунок В.11– Вимоги замовника 
 
Рисунок В.12 – Вимоги розробника 
117 
482.ЧДТУ 252477 90 01 6 
 
Рисунок В.13 –  Діаграма прецедентів 
 
Рисунок В.14 – Діаграма класів 
 
Рисунок В.15 – Діаграма пакетів 
118 
482.ЧДТУ 252477 90 01 7 
 
Рисунок В.16 – Діаграма компонентів  
 
Рисунок В.17 – Діаграма розгортання 
 
Рисунок В.18 – Діаграма діяльності 
119 
482.ЧДТУ 252477 90 01 8 
 
Рисунок В.19 – Діаграма послідовності 
 
Рисунок В.20 – Діаграма комунікації 
 
Рисунок В.21 – Діаграма скінченого автомату  
120 
482.ЧДТУ 252477 90 01 9 
 
Рисунок В.22 – Структурна схема 
 
Рисунок В.23 – Функціональна схема 
 
Рисунок В.24 – Логічна схема 
121 
482.ЧДТУ 252477 90 01 10 
 
Рисунок В.25 – Вигляд бази даних 
 
Рисунок В.26 – Інтерфейс головної сторінки 
 
Рисунок В.27 – Інтерфейс сторінки каталогу 
122 
482.ЧДТУ 252477 90 01 11 
 
Рисунок В.28 – Інтерфейс сторінки акселероматра 
 
Рисунок В.29 – Інтерфейс сторінки гіроскопу 
 
Рисунок В.30 – Інтерфейс сторінки магнометру 
123 
482.ЧДТУ 252477 90 01 12 
 
Рисунок В.31 – Інтерфейс сторінки пошуку полярної зірки 
 
Рисунок В.32 – Інтерфейс сторінки зоряного часу 
 
Рисунок В.33 – Результати тестувань  
124 
482.ЧДТУ 252477 90 01 13 
 
Рисунок В.34 – Результати тестувань(продовження) 
 
Рисунок В.35 – Інтерфейс гіроскопу 
 
Рисунок В.36 – Інтерфейс гіроскопу 
 
125