Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,18 @@
# Changelog

## v0.3.0 — Radio controls, command presets and CubeSat model

- добавлена раскрывающаяся схема подключения CC1101 к ESP32-WROOM-32;
- добавлена раскрывающаяся схема подключения E32-433T30D к ESP32-WROOM-32;
- добавлена настройка частоты, мощности и параметров радиоканала прямо из OpenMCC;
- RF-настройки сохраняются локально и формируют будущую команду `$CMD,RADIO,...` для ESP32-шлюза;
- добавлен запрос `RADIO_STATUS` для будущей прошивки радиошлюза;
- добавлен список готовых безопасных команд с пояснениями;
- 3D-модель переработана в более реалистичный 2U CubeSat с фиксированными солнечными панелями;
- удалена условная одиночная штыревая антенна, добавлена раскрытая рулеточная дипольная антенна с двумя противоположными плечами;
- добавлена документация `docs/RADIO_CONTROL_UI.md`;
- версия Windows-приложения повышена до 0.3.0.

## v0.2.0 — Windows desktop packaging

- добавлен desktop-host на Electron;
Expand Down
76 changes: 48 additions & 28 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# OpenMCC

**OpenMCC (Open Mission Control Center)** — локальный интерфейс учебного центра управления полётами для CubeSat/МКА. Проект работает офлайн, использует Web Serial API для связи с микроконтроллерами и постепенно расширяется до полноценной наземной станции с радиоканалом и автоматическим сопровождением спутника.
**OpenMCC (Open Mission Control Center)** — локальный интерфейс учебного центра управления полётами для CubeSat/МКА. Проект работает офлайн, использует последовательное соединение с микроконтроллерами и постепенно расширяется до полноценной наземной станции с радиоканалом и автоматическим сопровождением спутника.

OpenMCC можно запускать двумя способами:

Expand All @@ -9,23 +9,28 @@ OpenMCC можно запускать двумя способами:

## Что уже работает

- подключение Arduino/STM32/ESP32 через Web Serial;
- подключение Arduino/STM32/ESP32 через Web Serial или desktop-host;
- приём и разбор телеметрии;
- отправка команд;
- журнал событий;
- графики в реальном времени на Chart.js;
- 3D-модель CubeSat на Three.js;
- 3D-модель 2U CubeSat на Three.js с рулеточной дипольной антенной;
- отдельный интерфейс двухосевой поворотки AZ/EL;
- симулятор поворотного устройства без реальной механики;
- демонстрационная прошивка Arduino Uno/Nano для телеметрии;
- Windows desktop-host на Electron с нативным выбором последовательного порта;
- автоматическая сборка установщика через GitHub Actions.

## Что пока не реализовано

- реальный радиошлюз на ESP32-WROOM-32;
- драйвер CC1101;
- драйвер E32-433T30D;
- автоматическая сборка установщика через GitHub Actions;
- интерфейс выбора CC1101 или E32-433T30D;
- раскрывающиеся таблицы подключения радиомодулей к ESP32-WROOM-32;
- настройка RF-параметров в интерфейсе с локальным сохранением и формированием команды для будущего ESP32-шлюза;
- готовые примеры безопасных команд в центре команд.

## Что пока не реализовано полностью

- реальная прошивка радиошлюза ESP32-WROOM-32;
- драйвер CC1101 в ESP32;
- драйвер E32-433T30D в ESP32;
- фактическое применение RF-настроек на радиомодуле;
- единый пакетный протокол радиоканала с CRC/подтверждениями;
- TLE/SGP4;
- карта орбиты и радиогоризонт;
Expand All @@ -38,7 +43,7 @@ OpenMCC можно запускать двумя способами:
После публикации релиза пользователь скачивает файл вида:

```text
OpenMCC-Setup-0.2.0.exe
OpenMCC-Setup-0.3.0.exe
```

и запускает обычный мастер установки. Python, Node.js и отдельный браузер пользователю не требуются.
Expand Down Expand Up @@ -83,6 +88,19 @@ examples/arduino_avr_demo/OpenMCC_Arduino_AVR/OpenMCC_Arduino_AVR.ino

Загрузите его в Arduino Uno/Nano, подключите плату к ПК, выберите в OpenMCC `Arduino Uno` и `115200 бод`, затем нажмите **«Подключить устройство»**.

## Радиоканал

Наземный шлюз планируется на `ESP32-WROOM-32`.

В панели **«Радиоканал ЦУПа»** можно выбрать:

- `CC1101` — SPI;
- `E32-433T30D` — UART.

После выбора OpenMCC показывает рекомендуемое подключение, питание и параметры радиотракта. Подробно: `docs/RADIO_CONTROL_UI.md`.

До появления прошивки ESP32 кнопка **«Сохранить / передать в шлюз»** всегда сохраняет конфигурацию локально. Если ESP32 подключена, интерфейс дополнительно отправляет команду `$CMD,RADIO,...`; фактическое изменение настроек радиомодуля зависит от будущей прошивки шлюза.

## Архитектура

```text
Expand Down Expand Up @@ -110,27 +128,29 @@ examples/arduino_avr_demo/OpenMCC_Arduino_AVR/OpenMCC_Arduino_AVR.ino
- `docs/PROJECT_STATUS.md` — что сделано, что проверено и что осталось;
- `docs/ARCHITECTURE.md` — архитектура программной и аппаратной части;
- `docs/RADIO_LINK.md` — план двустороннего радиоканала CC1101 / E32;
- `docs/RADIO_CONTROL_UI.md` — подключение и управление радиомодулями из OpenMCC;
- `docs/DESKTOP_APP.md` — сборка, установка и публикация Windows-приложения;
- `CHANGELOG.md` — история заметных изменений.

## Основные модули

```text
index.html интерфейс OpenMCC
css/style.css базовые стили
css/layout.css адаптивная компоновка v0.2
css/panels.css оформление панелей v0.2
css/widgets.css элементы управления и подсказки v0.2
js/parser.js разбор входящих пакетов
js/logger.js журналирование
js/serial.js основное Web Serial-соединение
js/ui.js команды аппарату
js/charts.js графики Chart.js
js/rotator.js отдельное соединение с повороткой
js/satellite.js 3D-модель Three.js
js/help.js подсказки, навигация и встроенная справка
desktop/main.cjs Electron-host и Web Serial для desktop-версии
package.json конфигурация desktop-сборки
index.html интерфейс OpenMCC
css/style.css базовые стили
css/layout.css адаптивная компоновка
css/panels.css оформление панелей
css/widgets.css общие элементы управления
css/radio-controls.css RF-настройки и готовые команды v0.3
js/parser.js разбор входящих пакетов
js/logger.js журналирование
js/serial.js основное последовательное соединение
js/ui.js команды аппарату
js/charts.js графики Chart.js
js/rotator.js отдельное соединение с повороткой
js/satellite.js 3D-модель 2U CubeSat
js/help.js подсказки, RF UI и готовые команды
desktop/main.cjs Electron-host и Serial для desktop-версии
package.json конфигурация desktop-сборки
```

## Сборка Windows-установщика для разработчика
Expand All @@ -142,8 +162,8 @@ npm run dist:win

Результат создаётся в `release/`.

GitHub Actions также собирает установщик автоматически. При публикации тега вида `v0.2.0` workflow создаёт GitHub Release и загружает туда Windows installer.
GitHub Actions собирает установщик автоматически. После изменения версии `package.json` и слияния в `main` workflow может создать новый GitHub Release с установщиком соответствующей версии.

## Важный принцип проекта

OpenMCC должен оставаться независимым от конкретного радиомодуля. CC1101 и E32-433T30D будут скрыты за ESP32-WROOM-32, а интерфейс продолжит работать с единым протоколом телеметрии и команд.
OpenMCC должен оставаться независимым от конкретного радиомодуля. CC1101 и E32-433T30D скрываются за ESP32-WROOM-32, а интерфейс телеметрии и команд остаётся единым.
247 changes: 247 additions & 0 deletions css/radio-controls.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,247 @@
/* OpenMCC v0.3 — radio configuration and command presets */

#radioAdvancedControls {
display: grid;
gap: 12px;
margin-top: 12px;
}

.radioEmptyState {
display: grid;
gap: 5px;
padding: 14px;
border: 1px dashed rgba(71, 103, 137, .75);
border-radius: 10px;
background: rgba(5, 12, 23, .38);
}

.radioEmptyState strong { color: #d8e9f7; font-size: 12px; }
.radioEmptyState span { color: #7893ac; font-size: 11px; line-height: 1.5; }

.radioDetails {
overflow: hidden;
border: 1px solid rgba(43, 73, 108, .72);
border-radius: 11px;
background: rgba(5, 12, 23, .45);
}

.radioDetails summary {
padding: 11px 13px;
cursor: pointer;
color: #cfe8f5;
font-size: 11px;
font-weight: 700;
letter-spacing: .02em;
background: rgba(15, 29, 49, .7);
}

.radioDetails[open] summary { border-bottom: 1px solid rgba(43, 73, 108, .62); }
.radioDetailsBody { padding: 12px; }

.radioPinTable {
width: 100%;
border-collapse: collapse;
font-size: 10px;
}

.radioPinTable th,
.radioPinTable td {
padding: 7px 8px;
border-bottom: 1px solid rgba(39, 66, 98, .52);
text-align: left;
vertical-align: top;
}

.radioPinTable th {
color: #7fa1bd;
font-size: 9px;
letter-spacing: .07em;
text-transform: uppercase;
}

.radioPinTable td:first-child,
.radioPinTable td:nth-child(2) {
color: #d9eef8;
font-family: Consolas, monospace;
white-space: nowrap;
}

.radioPinTable td:last-child { color: #829bb2; }

.radioSafetyNote,
.radioRegulatoryNote {
margin: 10px 0 0;
color: #8da5bb;
font-size: 10px;
line-height: 1.55;
}

.radioRegulatoryNote {
padding: 9px 10px;
border-left: 2px solid rgba(255, 193, 7, .68);
background: rgba(255, 193, 7, .035);
}

.radioConfigCard {
padding: 13px;
border: 1px solid rgba(43, 73, 108, .75);
border-radius: 11px;
background: linear-gradient(180deg, rgba(9, 20, 35, .78), rgba(5, 12, 23, .72));
}

.radioConfigHeader {
display: flex;
align-items: center;
justify-content: space-between;
gap: 10px;
margin-bottom: 12px;
}

.radioConfigHeader > div { display: grid; gap: 3px; }
.radioConfigHeader span { color: #6f8ca8; font-size: 9px; letter-spacing: .09em; text-transform: uppercase; }
.radioConfigHeader strong { color: #dcecf8; font-size: 13px; }

.radioLocalBadge {
padding: 4px 7px;
border: 1px solid rgba(73, 112, 148, .6);
border-radius: 999px;
color: #7898b4 !important;
font-size: 8px !important;
white-space: nowrap;
}

.radioControlGrid {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 10px;
}

.radioControlGrid > label:not(.radioCheck) {
display: grid;
gap: 6px;
}

.radioControlGrid label > span {
color: #8099b0;
font-size: 9px;
font-weight: 700;
letter-spacing: .07em;
text-transform: uppercase;
}

.radioControlGrid select,
.radioControlGrid input[type="number"] {
width: 100%;
min-height: 38px;
padding: 0 10px;
color: #e4f1fb;
border: 1px solid rgba(45, 75, 109, .85);
border-radius: 8px;
background: #14223a;
}

.radioCheck {
display: flex;
align-items: center;
gap: 8px;
min-height: 38px;
padding: 8px 10px;
border: 1px solid rgba(45, 75, 109, .72);
border-radius: 8px;
background: rgba(20, 34, 58, .62);
}

.radioCheck input { accent-color: #00d9ff; }
.radioCheck span { margin: 0; }

.radioCommandPreview {
display: grid;
gap: 6px;
margin-top: 12px;
padding: 10px;
border: 1px solid rgba(42, 70, 102, .65);
border-radius: 9px;
background: rgba(3, 9, 17, .72);
}

.radioCommandPreview span {
color: #6d89a3;
font-size: 9px;
letter-spacing: .08em;
text-transform: uppercase;
}

.radioCommandPreview code {
overflow-wrap: anywhere;
color: #8feaff;
font: 600 10px/1.5 Consolas, monospace;
}

.radioActionRow {
display: grid;
grid-template-columns: 1.35fr 1fr;
gap: 8px;
margin-top: 10px;
}

.radioApplyButton,
.radioSecondaryButton {
min-height: 40px;
margin: 0;
border-radius: 8px;
font-size: 11px;
}

.radioApplyButton {
color: #041019;
border-color: #21dfff;
background: linear-gradient(135deg, #14d9ff, #62e8ff);
font-weight: 750;
}

.radioSecondaryButton {
color: #b4c9da;
border-color: rgba(56, 86, 119, .85);
background: rgba(17, 31, 52, .72);
}

.radioApplyStatus {
margin-top: 9px;
color: #7f98ae;
font-size: 10px;
line-height: 1.45;
}

.radioApplyStatus[data-state="saved"] { color: #8fd6b3; }
.radioApplyStatus[data-state="sent"] { color: #77e5ff; }
.radioApplyStatus[data-state="error"] { color: #ff8d8d; }

.commandPresetGroup { margin-top: 2px; }

.commandPresetRow {
display: grid;
grid-template-columns: minmax(0, 1.6fr) minmax(150px, .8fr);
gap: 8px;
}

.commandPresetRow select {
width: 100%;
min-height: 42px;
padding: 0 10px;
color: #e3f0fb;
border: 1px solid rgba(45, 75, 109, .85);
border-radius: 8px;
background: #14223a;
}

.commandPresetRow button { margin: 0; }
.commandPresetNote { margin-top: 8px; color: #7892a8; font-size: 10px; line-height: 1.45; }

@media (max-width: 720px) {
.radioControlGrid,
.radioActionRow,
.commandPresetRow { grid-template-columns: 1fr; }
.radioPinTable { font-size: 9px; }
.radioPinTable th,
.radioPinTable td { padding: 6px; }
}
Loading
Loading