Metadata-Version: 2.4
Name: emgs-app
Version: 1.0.9
Summary: EMGS PyQt6 desktop application (BLE + asyncio).
Author: rr_emg contributors
License: Proprietary
Project-URL: Homepage, https://github.com/o0fung/RR_EMG
Project-URL: Repository, https://github.com/o0fung/RR_EMG
Project-URL: Issues, https://github.com/o0fung/RR_EMG/issues
Keywords: emg,ble,pyqt6,imu,sensor
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Science/Research
Classifier: Topic :: Scientific/Engineering :: Medical Science Apps.
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Operating System :: MacOS
Classifier: Operating System :: POSIX :: Linux
Classifier: Operating System :: Microsoft :: Windows
Requires-Python: <3.14,>=3.10
Description-Content-Type: text/markdown
Requires-Dist: PyQt6==6.9.1
Requires-Dist: qasync==0.27.1
Requires-Dist: bleak==0.22.3
Requires-Dist: pyqtgraph==0.13.7
Requires-Dist: numpy==2.3.1
Requires-Dist: rich==14.1.0
Requires-Dist: pyobjc-core==10.3.2; platform_system == "Darwin"
Requires-Dist: pyobjc-framework-Cocoa==10.3.2; platform_system == "Darwin"
Requires-Dist: pyobjc-framework-CoreBluetooth==10.3.2; platform_system == "Darwin"
Requires-Dist: pyobjc-framework-libdispatch==10.3.2; platform_system == "Darwin"
Provides-Extra: ml
Requires-Dist: scipy; extra == "ml"
Requires-Dist: scikit-learn; extra == "ml"
Requires-Dist: joblib; extra == "ml"

# EMGS PyQt6 App (next-gen, clean rewrite)

This repository contains:

- `backup/`: historical working versions (latest reference: `backup/EMGS_v4/`)
- `emgs_app/`: **new clean rewrite** (PyQt6 + asyncio) intended to become the main application going forward

## Goal / current focus

Build a **pythonic, readable, extensible** desktop application that makes it easy to:

- Connect to **multiple EMGS sensors simultaneously** (initial target: 4–8, designed to scale higher)
- Manage connections robustly across **macOS (primary)**, Linux, Windows
- Stream EMG + 9-DoF IMU data across **many devices**, with a **comparable timestamp x-axis**
- Provide a clean landing experience (simple, intuitive), with room to grow into HMI features later

## Installation

### Option A: Windows GUI installer (recommended for most end users)

For non-technical users, download the latest `emgs-app-<version>-setup.exe` from
[GitHub Releases](https://github.com/o0fung/RR_EMG/releases), then:

1. Double-click the installer
2. Follow the setup wizard
3. Launch **EMGS App** from Start Menu / Desktop shortcut

If you do not want to install, use the portable zip artifact:

- `emgs-app-<version>-portable-win64.zip`
- Unzip and run `emgs.exe`

### Option B: macOS app download

Download the latest macOS artifact from
[GitHub Releases](https://github.com/o0fung/RR_EMG/releases):

- `emgs-app-<version>-macos.dmg` (recommended)
- or `emgs-app-<version>-macos-app.zip`

Install from `.dmg`:

1. Open the `.dmg`
2. Drag `EMGS App.app` to your `Applications` folder (or run directly for quick test)
3. Launch from Finder / Applications

> Note: unsigned builds may show a Gatekeeper warning. If needed, right-click the app,
> choose **Open**, then confirm.

### Option C: Install with pipx (recommended CLI install)

[pipx](https://pipx.pypa.io/) installs the app in its own isolated environment and puts the
`emgs` command on your PATH. Great for desktop apps you just want to *run*:

```bash
pipx install emgs-app
```

If your system default Python is 3.14, install with Python 3.13 (or 3.12) instead:

```bash
brew install python@3.13
pipx install --python /opt/homebrew/bin/python3.13 emgs-app
```

Upgrade:

```bash
pipx upgrade emgs-app
```

### Option D: Install from PyPI (`pip`)

The simplest way -- no GitHub account or repo access required:

```bash
pip install emgs-app
```

Upgrade to the latest version:

```bash
pip install --upgrade emgs-app
```

### Option E: Install from GitHub (latest development version)

```bash
pip install "emgs-app @ git+https://github.com/o0fung/RR_EMG.git"
```

Upgrade to the latest commit:

```bash
pip install --upgrade "emgs-app @ git+https://github.com/o0fung/RR_EMG.git"
```

### Option F: Developer install (editable, local checkout)

```bash
git clone https://github.com/o0fung/RR_EMG.git
cd RR_EMG
python -m venv .venv
source .venv/bin/activate  # Windows: .venv\Scripts\activate
pip install -e .
```

### Optional ML extras

Add fatigue-detection / analysis dependencies:

```bash
pip install "emgs-app[ml]"
```

## Build installer artifacts (maintainers)

### Windows

From repository root on Windows PowerShell:

```powershell
./scripts/release/build_windows.ps1 -Version 0.1.0
```

Outputs:

- `dist/emgs-app-<version>-portable-win64.zip`
- `dist/installer/emgs-app-<version>-setup.exe` (when `iscc` is available)

Notes:

- PyInstaller spec: `packaging/pyinstaller/emgs_app.spec`
- Inno Setup script: `packaging/windows/emgs_app.iss`
- Install Inno Setup to provide `iscc` for GUI installer builds.

### macOS

From repository root on macOS:

```bash
./scripts/release/build_macos.sh 0.1.0
```

Outputs:

- `dist/emgs-app-<version>-macos-app.zip`
- `dist/emgs-app-<version>-macos.dmg`

Optional signing/notarization env vars for local maintainer builds:

- `MACOS_SIGNING_IDENTITY` (Developer ID Application identity name)
- `APPLE_ID`
- `APPLE_TEAM_ID`
- `APPLE_APP_PASSWORD` (app-specific password)

If these are not set, the script builds unsigned artifacts.

## Release guide (maintainers)

### 1) Prepare version

Update `emgs_app/__init__.py`:

```python
__version__ = "0.3.1"
```

Commit and push your changes to `main`.

### 2) Create GitHub release

1. Go to [GitHub Releases](https://github.com/o0fung/RR_EMG/releases)
2. Click **Draft a new release**
3. Tag format: `v0.3.1` (the `v` prefix is expected by CI)
4. Choose target branch (usually `main`)
5. Add release notes and click **Publish release**

Publishing the release triggers `.github/workflows/publish.yml`, which builds and uploads:

- PyPI wheel + sdist
- Windows `setup.exe` + portable zip
- macOS `.dmg` + app zip

macOS signing/notarization in CI is enabled when these repository secrets are configured:

- `MACOS_CERTIFICATE_P12_BASE64` (base64-encoded Developer ID Application `.p12`)
- `MACOS_CERTIFICATE_PASSWORD`
- `MACOS_KEYCHAIN_PASSWORD`
- `MACOS_SIGNING_IDENTITY`
- `APPLE_ID`
- `APPLE_TEAM_ID`
- `APPLE_APP_PASSWORD`

### 3) Verify release artifacts

After workflow completes, confirm assets are present on the release page:

- `emgs-app-<version>-setup.exe`
- `emgs-app-<version>-portable-win64.zip`
- `emgs-app-<version>-macos.dmg`
- `emgs-app-<version>-macos-app.zip`
- `emgs_app-<version>-py3-none-any.whl` and source tarball

Also verify PyPI page:

- https://pypi.org/project/emgs-app/

## How users download and install

### Windows non-technical users

1. Open [Releases](https://github.com/o0fung/RR_EMG/releases/latest)
2. Under **Assets**, download `emgs-app-<version>-setup.exe`
3. Double-click installer and follow wizard
4. Launch **EMGS App** from Start Menu

### macOS users

1. Open [Releases](https://github.com/o0fung/RR_EMG/releases/latest)
2. Download `emgs-app-<version>-macos.dmg`
3. Open dmg, drag app to Applications, launch
4. If macOS blocks launch, right-click app -> **Open** (unsigned builds only)

If the app exits immediately, run the inner executable from Terminal to inspect errors:

```bash
"/Applications/EMGS App.app/Contents/MacOS/EMGS App"
```

### CLI / developer users

Use `pipx` (recommended app install):

```bash
pipx install emgs-app
```

Or use `pip`:

```bash
pip install emgs-app
```

## Usage

### Run

```bash
emgs
```

### Run without hardware (demo mode)

```bash
EMGS_BACKEND=sim emgs
```

## Firmware reference (ground truth)

The firmware source used to confirm packet formats and timestamp semantics is stored in:

- `doc/RnD/Software/EMGS - prod - s132 - RGB/` (nRF52 firmware)
- DSP-side EMG/RMS/SNR implementation (2nd MCU) is documented in:
  - `doc/RnD/EMG_DSP_V02.6.0_protocol.md`

Key takeaways (important for decoding + synchronization):

- **Device timestamps are 10 ms resolution**: firmware timebase increments by 10 (`m_timeSeedApp += 10`).
- **EMG packet (`'S''E'`) layout** is defined by `C_PACKET_*` in `application.h` and raw-record comments in `memory.c`.
- **IMU packets (`'S''I'`) are batched** and sent on a timer (`C_IMU_PACKET_INTERVAL = 150ms`), so packet spacing is not “clean 100 Hz”.

AKR firmware reference for robot telemetry + CLI:

- **RR telemetry framing/source**: `backup/akr_code_v7/Bsp/rr_telem.h`, `backup/akr_code_v7/Bsp/rr_telem.c`
- **CPM packet update (v7)**: payload includes `blocked_event` and `blocked_consecutive`
- **CLI text/source**: `backup/akr_code_v7/Bsp/rr_status.c`
- **CLI wording note**: measure logs now use `MEASURE event:` (legacy `TEST event:` may still appear in older backups)

Python decoder implementation (kept isolated on purpose):

- `emgs_app/ble/device.py`

## Architecture (high level)

The new app is intentionally split into small modules:

- `emgs_app/ble/`: BLE + protocol + device state
  - `protocol.py`: centralized opcodes/UUIDs (adapted from `backup/EMGS_v4/protocol.py`)
  - `manager.py`: scan/connect/stream orchestration for many devices (asyncio)
  - `device.py`: per-device state, buffers, minimal decode hooks
- `emgs_app/gui/`: PyQt6 UI (model/view, scalable layouts)
  - `main_window.py`: window + navigation + actions
  - `models.py`: Qt models for device list/table
  - `widgets.py`: reusable widgets (badges, plots, log view)
- `emgs_app/app.py`: Qt + asyncio bootstrap (via `qasync`)

## Plot timebase (x-axis comparable between devices)

Signals plots use a **shared device-time origin**:

- Samples are stored with **absolute device timestamps** (seconds).
- UI subtracts a **single global origin** (earliest device timestamp seen) so all devices share “t=0”.

Code:

- `emgs_app/gui/main_window.py`: chooses `_global_time_origin_s`
- `emgs_app/gui/widgets.py`: tiles subtract origin for display

## OS notes / BLE troubleshooting

- **macOS (primary target)**:
  - The first BLE scan/connect may trigger Bluetooth permission prompts.
  - If scans return empty, verify Bluetooth is enabled and the app has permission in System Settings.
- **Linux**:
  - BLE requires BlueZ + a working adapter; you may need permissions (often via running under a user with access to BLE).
  - If you see adapter errors, confirm `bluetoothd` is running.
- **Windows**:
  - Use a BLE-capable adapter; some dongles/drivers can be flaky with many concurrent connections.

## Notes for future Cursor agents / collaborators

- **Historical references**:
  - `backup/EMGS_v4/`: multi-device UI + qasync baseline
  - `backup/EMGS_v1/`: older parsing ideas (not fully accurate for current firmware)
- **Current truth for packet formats**: `firmware_ref/`
- **AKR packet/CLI truth (current)**: `backup/akr_code_v7/Bsp/rr_telem.*`, `backup/akr_code_v7/Bsp/rr_status.c`
- **Where to change decode**: `emgs_app/ble/device.py`
- **Where to change commands/settings**: `emgs_app/ble/protocol.py`, `emgs_app/ble/manager.py`
- **Backend switch**: `EMGS_BACKEND=sim`

## Supported configuration (currently implemented)

In the GUI: select a connected device → **Settings…**

- **BLE**
  - Set BLE advertising name (12 bytes)
  - Set connection interval (min/max)
- **Streaming**
  - Start/stop stream
  - Time sync (sets device timestamp)
- **EMG**
  - EMG option / mode (disable / RMS-only / RMS+RAW)
  - EMG threshold (uint16)
  - Power-safe level (%) (low-battery threshold)
  - Out-of-storage threshold (%)
- **IMU / ICM20948**
  - Enable modes (ACC/GYR/MAG/QUAT)
  - Set/query freq (ACC/GYR/MAG)
  - Set/query scale (ACC/GYR)
  - Get/set bias (acc/gyr/mag) + auto-save toggle

## Planned next steps (recommended)

- Finish EMG RAW numeric interpretation if needed (firmware copies raw bytes through; endianness is sensor-side).
- Add per-device configuration persistence (QSettings) and per-project profiles.
- Add a recording pipeline (write `.csv` / `.h5`) and session metadata
- Add HMI experiments as plugins (gesture mapping, virtual input devices, etc.)

