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
78 changes: 70 additions & 8 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
name: Release

# Tag push (e.g. v0.1.0) -> signed APK + AAB, attached to a GitHub Release.
# Tag push (e.g. v0.1.0) -> Android APK/AAB + Linux .deb + Windows .msi on GitHub Release.
on:
push:
tags: ['v*']
workflow_dispatch:

jobs:
release:
android:
runs-on: ubuntu-latest
permissions:
contents: write # required to create the GitHub Release
steps:
- uses: actions/checkout@v4

Expand Down Expand Up @@ -42,16 +40,80 @@ jobs:
RELEASE_KEY_PASSWORD: ${{ secrets.RELEASE_KEY_PASSWORD }}
run: ./gradlew :app:assembleRelease :app:bundleRelease --stacktrace

- name: Upload artifacts
- name: Upload Android artifacts
uses: actions/upload-artifact@v4
with:
name: rtk-router-release
name: android
path: |
app/build/outputs/apk/release/app-release.apk
app/build/outputs/bundle/release/app-release.aab

- name: Create GitHub Release (APK for sideload)
desktop-linux:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Set up JDK 17
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: '17'

- name: Set up Gradle
uses: gradle/actions/setup-gradle@v3

- name: Build Linux .deb
run: ./gradlew :desktopApp:packageReleaseDeb --stacktrace -Dorg.gradle.configureondemand=true

- name: Upload Linux .deb
uses: actions/upload-artifact@v4
with:
name: desktop-linux
path: desktopApp/build/compose/binaries/main-release/deb/*.deb

desktop-windows:
runs-on: windows-latest
steps:
- uses: actions/checkout@v4

- name: Set up JDK 17
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: '17'

- name: Set up Gradle
uses: gradle/actions/setup-gradle@v3

- name: Build Windows .msi
shell: pwsh
run: .\gradlew :desktopApp:packageReleaseMsi --stacktrace -Dorg.gradle.configureondemand=true

- name: Upload Windows .msi
uses: actions/upload-artifact@v4
with:
name: desktop-windows
path: desktopApp/build/compose/binaries/main-release/msi/*.msi

publish:
needs: [android, desktop-linux, desktop-windows]
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/download-artifact@v4
with:
path: dist
merge-multiple: true

- name: Collect release files
run: |
mkdir -p release
find dist -type f \( -name '*.apk' -o -name '*.aab' -o -name '*.deb' -o -name '*.msi' \) -exec cp -v {} release/ \;
ls -la release/

- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
files: app/build/outputs/apk/release/app-release.apk
files: release/*
generate_release_notes: true
12 changes: 11 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,19 @@ NTRIP caster ──RTCM3/TCP──▶ [폰 NTRIP client] ──▶ [USB serial T
| M5 | 운영 | 수신기 프로파일 다중 + 세션 로깅 |

## 빌드 / 실행
- APK: `./gradlew assembleDebug` (phone-sensor 패턴) — 산출물 `app/build/outputs/apk/debug/`.

### Android
- APK: `./gradlew assembleDebug` — 산출물 `app/build/outputs/apk/debug/`.
- 폰: NTRIP 설정(host/port/mount/auth) 입력 → USB 수신기 연결(OTG) → baud 선택 → START.

### Desktop (Linux & Windows)
- 실행: `./gradlew :desktopApp:run` (JDK 17)
- Linux: `/dev/ttyACM0`, `/dev/ttyUSB0` — 시리얼 권한은 앱 내 pkexec/sudo 또는 `dialout` 그룹
- Windows: `COM*` 포트 선택 (드라이버 설치 후)
- 릴리즈: 태그 `v*` push → GitHub Release에 APK/AAB + `.deb` + `.msi` 자동 첨부
- 로컬 패키지: `./gradlew :desktopApp:packageReleaseDeb` (Linux) / `packageReleaseMsi` (Windows)
- 상세: [desktopApp/README.md](desktopApp/README.md)

## 참조
- phone-sensor-stream: 같은 Kotlin/Compose 스택, USB/GNSS(gnsstest) 코드 재사용 후보.
- NovAtel: RTKASSIST / RTCM 입력 포트 문서. u-blox: u-center, UBX-CFG.
2 changes: 2 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,7 @@
plugins {
id("com.android.application") version "8.5.2" apply false
id("org.jetbrains.kotlin.android") version "1.9.24" apply false
id("org.jetbrains.kotlin.jvm") version "1.9.24" apply false
id("org.jetbrains.kotlin.plugin.serialization") version "1.9.24" apply false
id("org.jetbrains.compose") version "1.6.11" apply false
}
71 changes: 71 additions & 0 deletions desktopApp/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# RTK Router — Desktop (Linux & Windows)

NTRIP caster에서 RTCM3 보정 데이터를 받아 USB/serial로 GNSS 수신기(u-blox F9P 등)에 주입하는 GUI 앱.

| OS | Serial port | Package |
|----|-------------|---------|
| Linux | `/dev/ttyACM*`, `/dev/ttyUSB*` | `.deb` |
| Windows | `COM*` | `.msi` |

## Build & Run (dev)

JDK 17 required.

```bash
# from repo root
./gradlew :desktopApp:run
```

## Release packages (local)

```bash
# Linux .deb
./gradlew :desktopApp:packageReleaseDeb

# Windows .msi (run on Windows)
.\gradlew :desktopApp:packageReleaseMsi
```

Outputs under `desktopApp/build/compose/binaries/main-release/`.

## GitHub Release (CI)

`v*` tag push (e.g. `v0.1.0`) triggers [.github/workflows/release.yml](../.github/workflows/release.yml):

- Android: `app-release.apk`, `app-release.aab`
- Linux: `rtk-router_*_amd64.deb`
- Windows: `rtk-router-*.msi`

```bash
git tag v0.1.0
git push origin v0.1.0
```

## Linux serial permissions

`/dev/tty*` 장치에 읽기/쓰기 권한이 필요합니다.

1. **앱 내 (임시)**: START 시 권한이 없으면 다이얼로그에서
- **pkexec로 권한 부여** — `chmod a+rw /dev/ttyACM0`
- **sudo로 권한 부여** — 앱 내 비밀번호 입력
- **dialout 영구 추가** — `usermod -aG dialout $USER` (재로그인)

2. **수동 (권장, 영구)**:

```bash
sudo usermod -aG dialout $USER
# log out and back in
```

## Windows serial

USB-UART 또는 u-blox CDC 드라이버 설치 후 Device Manager에서 COM 포트 번호를 확인하세요. 별도 권한 설정은 필요 없습니다.

## Device notes

| Device | Linux | Windows | Baud |
|--------|-------|---------|------|
| u-blox ZED-F9P (USB CDC) | `/dev/ttyACM0` | `COM3` (varies) | ignored (native CDC) |
| USB-UART (FTDI/CP210x/CH340) | `/dev/ttyUSB0` | `COM4` (varies) | must match receiver UART |

Settings: Linux `~/.config/rtk-router/settings.json`, Windows `%USERPROFILE%\.config\rtk-router\settings.json`.
42 changes: 42 additions & 0 deletions desktopApp/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import org.jetbrains.compose.desktop.application.dsl.TargetFormat

plugins {
kotlin("jvm")
id("org.jetbrains.compose")
kotlin("plugin.serialization")
}

group = "com.onlyti.rtkrouter"
version = "0.1.0"

kotlin {
jvmToolchain(17)
}

dependencies {
implementation(compose.desktop.currentOs)
implementation(compose.material3)
implementation(compose.materialIconsExtended)

implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.3")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.1")

// Linux /dev/tty* and Windows COM* serial access.
implementation("com.fazecast:jSerialComm:2.10.4")
}

compose.desktop {
application {
mainClass = "com.onlyti.rtkrouter.desktop.MainKt"
buildTypes.release.proguard {
isEnabled.set(false)
}
nativeDistributions {
targetFormats(TargetFormat.Deb, TargetFormat.Msi)
packageName = "rtk-router"
packageVersion = "0.1.0"
description = "NTRIP RTCM router for GNSS receivers"
vendor = "onlyti"
}
}
}
17 changes: 17 additions & 0 deletions desktopApp/src/main/kotlin/com/onlyti/rtkrouter/desktop/Main.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.onlyti.rtkrouter.desktop

import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Window
import androidx.compose.ui.window.application
import androidx.compose.ui.window.rememberWindowState
import com.onlyti.rtkrouter.desktop.ui.DesktopApp

fun main() = application {
Window(
onCloseRequest = ::exitApplication,
title = "RTK Router",
state = rememberWindowState(width = 920.dp, height = 900.dp),
) {
DesktopApp()
}
}
Loading
Loading