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 Інженерія програмного забезпечення (Інженерія програмного забезпечення) |
Files in This Item:
| File | Description | Size | Format | |
|---|---|---|---|---|
| Кваліфікаційна робота магістра Веретільника Віталія Олеговича.pdf Restricted Access | 5.26 MB | Adobe PDF | View/Open Request a copy |
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