Skip to content
Open
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
11 changes: 11 additions & 0 deletions .claude/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"tools": {
"gh": {
"version": "2.62.0",
"path": "~/.local/bin/gh",
"description": "GitHub CLI for workflow monitoring",
"required": false,
"setup_command": "See .claude/hooks/SessionStart"
}
}
}
6 changes: 3 additions & 3 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: Docs

on:
push:
branches: [ master, develop, 'dev/*' ]
branches: [ master, develop, 'dev/*', 'claude/*' ]
pull_request:
branches: [ master, develop ]

Expand All @@ -11,12 +11,12 @@ jobs:
runs-on: ubuntu-22.04
steps:
- name: Checkout repo
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Set up Python
uses: actions/setup-python@v4
uses: actions/setup-python@v5
with:
python-version: 3.8

Expand Down
19 changes: 15 additions & 4 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: Test

on:
push:
branches: [ master, develop, 'dev/*' ]
branches: [ master, develop, 'dev/*', 'claude/*' ]
pull_request:
branches: [ master, develop ]

Expand All @@ -24,7 +24,7 @@ jobs:
python_version: '3.10'

steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- name: Install APT dependencies
if: "startsWith(runner.os, 'Linux')"
run: |
Expand All @@ -45,7 +45,7 @@ jobs:
# Remove the osx-arm64 environment
conda deactivate
conda env remove -n eeg-expy-full --yes

# Create osx-64 platform with audio support
conda create -v --platform osx-64 -n eeg-expy-full python=${PYTHON_VERSION}
conda activate eeg-expy-full
Expand All @@ -58,6 +58,17 @@ jobs:
run: |
conda install --force-reinstall numpy

- name: Debug numpy dependencies (Windows only)
if: matrix.os == 'windows-latest'
run: |
pip install pipdeptree
echo "=== Installed numpy version ==="
python -c "import numpy; print(f'numpy {numpy.__version__} from {numpy.__file__}')" || echo "numpy import failed"
echo "=== Packages depending on numpy ==="
pipdeptree -r -p numpy
echo "=== PsychoPy dependencies ==="
pipdeptree -p psychopy | head -50

- name: Run eegnb install test
run: |
if [ "$RUNNER_OS" == "Linux" ]; then
Expand Down Expand Up @@ -88,7 +99,7 @@ jobs:
python_version: [3.9]

steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- name: Install conda
uses: conda-incubator/setup-miniconda@v3
with:
Expand Down
156 changes: 156 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Overview

EEG-ExPy is a Python library for cognitive neuroscience experiments using consumer-grade EEG devices. It provides a unified framework for running classic EEG experiments (N170, P300, SSVEP, etc.) with devices like Muse, OpenBCI, and others. The goal is to democratize cognitive neuroscience research by making it accessible with affordable hardware.

## Claude Code Configuration

The `.claude/` directory contains configuration for Claude Code sessions:

### `.claude/config.json`
- **GitHub CLI (gh)**: Version 2.62.0, configured for workflow monitoring
- Path: `~/.local/bin/gh`
- Optional tool (not required)
- Setup instructions in `.claude/hooks/SessionStart`

## Development Commands

### Environment Setup
```bash
# Install in development mode with all dependencies
pip install --use-pep517 .[full]

# Install specific dependency groups
pip install -e .[streaming] # For EEG streaming
pip install -e .[stimpres] # For stimulus presentation
pip install -e .[streamstim] # Both streaming and stimulus
pip install -e .[docsbuild] # For documentation building
```

### Testing and Quality Control
```bash
# Run tests
make test
# OR: pytest --cov=eegnb --cov-report=term --cov-report=xml --cov-report=html --nbval-lax --current-env

# Run type checking (excludes visual_cueing due to errors)
make typecheck
# OR: python -m mypy --exclude 'examples/visual_cueing'

# Run single test file
pytest tests/test_specific.py
```

### Documentation
```bash
# Build documentation
make docs
# OR: cd doc && make html

# Clean documentation build
make clean
# OR: cd doc && make clean
```

### CLI Usage
```bash
# Run experiments via CLI
eegnb runexp --experiment visual_n170 --eegdevice muse2016 --recdur 120
# OR: eegexpy runexp --experiment visual_n170 --eegdevice muse2016 --recdur 120

# Interactive mode
eegnb runexp --prompt
```

## Architecture

### Core Components

1. **BaseExperiment Class** (`eegnb/experiments/Experiment.py:29`)
- Abstract base class for all experiments
- Key parameters: `n_trials`, `iti` (inter-trial interval), `soa` (stimulus onset asynchrony), `jitter`
- Subclasses must implement `load_stimulus()` and `present_stimulus()` methods
- Supports both standard displays and VR via `use_vr` parameter

2. **EEG Device Abstraction** (`eegnb/devices/eeg.py`)
- Unified interface for multiple EEG devices via BrainFlow and muselsl
- Supported devices: Muse, OpenBCI (Cyton/Ganglion), g.tec Unicorn, BrainBit, synthetic
- Handles device-specific streaming and marker insertion

3. **CLI Interface** (`eegnb/cli/__main__.py`)
- Command-line interface using Click
- Entry points: `eegnb` and `eegexpy`
- Supports interactive prompts and direct parameter specification

### Experiment Implementation Pattern

Each experiment inherits from `BaseExperiment` and implements:
- `load_stimulus()`: Load visual/auditory stimuli
- `present_stimulus(idx)`: Present stimulus for trial idx and push EEG markers

Example from N170 experiment (`eegnb/experiments/visual_n170/n170.py:21`):
```python
class VisualN170(Experiment.BaseExperiment):
def load_stimulus(self):
# Load face and house images
self.faces = list(map(load_image, glob(os.path.join(FACE_HOUSE, "faces", "*_3.jpg"))))
self.houses = list(map(load_image, glob(os.path.join(FACE_HOUSE, "houses", "*.3.jpg"))))
return [self.houses, self.faces]

def present_stimulus(self, idx: int):
label = self.trials["parameter"].iloc[idx]
image = choice(self.faces if label == 1 else self.houses)
image.draw()
if self.eeg:
self.eeg.push_sample(marker=self.markernames[label], timestamp=time())
self.window.flip()
```

### Available Experiments

Located in `eegnb/experiments/`:
- `visual_n170/` - Face/object recognition ERP
- `visual_p300/` - Oddball paradigm ERP
- `visual_ssvep/` - Steady-state visual evoked potentials
- `auditory_oddball/` - Auditory ERP paradigms
- `visual_cueing/` - Spatial attention cueing
- `visual_gonogo/` - Go/No-go task
- `somatosensory_p300/` - Tactile P300

### Dependencies and Requirements

The `requirements.txt` is organized into sections:
- **Analysis**: Core analysis tools (MNE, scikit-learn, pandas)
- **Streaming**: EEG device streaming (brainflow, muselsl, pylsl)
- **Stimpres**: Stimulus presentation (PsychoPy, psychtoolbox)
- **Docsbuild**: Documentation building (Sphinx, etc.)

### Platform-Specific Notes

- **macOS**: Uses `pyobjc==7.3` for GUI support
- **Windows**: Includes `psychxr` for VR support, `pywinhook` for input handling
- **Linux**: Requires `pyo>=1.0.3` for audio, additional system dependencies for PsychoPy

### Data Storage

- Default data directory: `eegnb.DATA_DIR`
- Filename generation: `eegnb.generate_save_fn()`
- Analysis utilities in `eegnb/analysis/`

### Configuration

- PsychoPy preferences set to PTB audio library with high precision latency mode
- Test configuration in `pyproject.toml` with pytest coverage and nbval for notebooks
- Makefile provides convenient commands for common tasks

### Testing Strategy

- pytest with coverage reporting
- nbval for notebook validation
- Excludes `examples/**.py` and `baseline_task.py` from tests
- Type checking with mypy (excludes visual_cueing)

This architecture enables rapid development of new experiments while maintaining consistency across the experimental framework and supporting diverse EEG hardware platforms.
6 changes: 4 additions & 2 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@

scikit-learn>=0.23.2
pandas>=1.1.4
numpy>=1.26.0; python_version >= "3.9"
# psychxr build pinned to this version of numpy.
numpy>=1.26,<1.27; python_version >= "3.9"
numpy<=1.24.4; python_version == "3.8"
mne>=0.20.8
seaborn>=0.11.0
Expand Down Expand Up @@ -60,7 +61,8 @@ ffpyplayer==4.5.2 # 4.5.3 fails to build as wheel.
psychtoolbox
scikit-learn>=0.23.2
pandas>=1.1.4
numpy>=1.26.0; python_version >= "3.9"
# psychxr build pinned to this version of numpy.
numpy>=1.26,<1.27; python_version >= "3.9"
numpy==1.24.4; python_version == "3.8"
mne>=0.20.8
seaborn>=0.11.0
Expand Down
Loading