# VLCSim - Visible Light Communication Simulator

![Python Version](https://img.shields.io/badge/python-3.9+-blue.svg)
![Poetry](https://img.shields.io/badge/poetry-2.3+-blue.svg)
![Coverage](https://img.shields.io/badge/coverage-72%25-brightgreen.svg)
![Tests](https://img.shields.io/badge/tests-166%20passing-brightgreen.svg)
![License](https://img.shields.io/badge/license-MIT-blue.svg)
![GitLab](https://img.shields.io/badge/gitlab-simvlc-orange.svg)
[![PyPI](https://img.shields.io/badge/PyPI-vlcSim-blue.svg)](https://pypi.org/project/vlcSim/)

VLCSim is an **Event-Oriented simulator** package for **Visible Light Communication (VLC)** systems. It provides a comprehensive framework for simulating dynamic VLC environments with support for resource allocation algorithms, RF fallback, and flexible room configurations.

## Features

- **Dynamic Environment**: Simulate real-time connections with arrivals and departures
- **Hybrid Communication**: VLC with RF fallback support
- **Flexible Resource Allocation**: Implement custom allocation algorithms
- **TDM Frame Management**: Time-Division Multiplexing with configurable slices
- **Configurable Parameters**: Customize VLC/room parameters for different scenarios
- **Event-Driven Simulation**: Efficient discrete event simulation engine
- **Performance Metrics**: Built-in SNR and capacity calculations

## Installation

### From PyPI (Recommended)

```bash
# Install latest stable version
pip install vlcSim

# Or with Poetry
poetry add vlcSim
```

### From Source (Development)

```bash
git clone https://gitlab.com/DaniloBorquez/simvlc.git
cd simvlc

# Using Poetry (recommended)
poetry install

# Or using pip
pip install -e .
```

### Requirements

**Runtime dependencies:**
- `python` >= 3.9
- `numpy` >= 1.24.0: Numerical computations and random number generation

**Development dependencies:**
- `pytest` >= 7.4.0: Testing framework
- `pytest-cov` >= 4.1.0: Coverage reporting

See [pyproject.toml](pyproject.toml) for complete dependency specifications.

## Quick Start

```python
from vlcsim import Simulator, VLed, RF

# Create a 10x10x3m room with 20 grids and 0.8 reflection coefficient
sim = Simulator(10.0, 10.0, 3.0, 20, 0.8)

# Add VLed access points
vled = VLed(5.0, 5.0, 3.0, 2, 2, 20, 60)  # x, y, z, nLedsX, nLedsY, power, theta
vled.sliceTime = 0.1
vled.slicesInFrame = 10
sim.scenario.addVLed(vled)

# Add RF access point as fallback
rf = RF(5.0, 5.0, 1.0)
rf.sliceTime = 0.1
rf.slicesInFrame = 10
sim.scenario.addRF(rf)

# Configure simulation parameters
sim.lambdaS = 3  # Arrival rate
sim.mu = 10  # Service rate
sim.goalConnections = 1000

# Initialize and run
sim.init()
sim.run()
```

## Core Concepts

### Events

The simulator uses 5 types of events:

| Event         | Description                                  |
| ------------- | -------------------------------------------- |
| **ARRIVE**    | New connection arrives to the system         |
| **RESUME**    | Connection begins transmission               |
| **PAUSE**     | Connection pauses transmission               |
| **DEPARTURE** | Connection ends transmission                 |
| **RETRYING**  | Unallocated connection attempts to reconnect |

### Scenario Components

- **VLed**: Visible Light LED access points with configurable power and beam angle
- **RF**: Radio Frequency access points for fallback communication
- **Receiver**: Mobile devices with photodetector capabilities
- **Connection**: Represents a data transmission session with capacity requirements

### Resource Allocation

VLCSim supports custom resource allocation algorithms. Implement your own strategy by defining a function that assigns connections to access points and frame slices.

## Advanced Example

```python
from vlcsim import *

def custom_allocation_algorithm(receiver, connection: Connection, 
                                scenario: Scenario, controller: Controller):
    """Custom allocation: best VLed or RF fallback if overloaded"""
    vleds = scenario.vleds
    capacities = [scenario.capacityVled(receiver, vled) for vled in vleds]
    best_idx = capacities.index(max(capacities))
    
    # Use RF if VLed overloaded or no capacity
    if (controller.numberOfActiveConnections(vleds[best_idx]) > 5 
        or capacities[best_idx] == 0):
        connection.AP = scenario.rfs[0]
        connection.receiver.capacityFromAP = scenario.capacityRf(receiver, connection.AP)
    else:
        connection.AP = vleds[best_idx]
        connection.receiver.capacityFromAP = capacities[best_idx]
    
    # Calculate required slices
    numberOfSlices = connection.numberOfSlicesNeeded(
        connection.capacityRequired, connection.receiver.capacityFromAP
    )
    
    # Assign slices in frames
    actualSlice = connection.nextSliceInAPWhenArriving(connection.AP)
    assigned = 0
    
    # Try current frame
    for slice in range(actualSlice, connection.AP.slicesInFrame):
        if (not controller.framesState(connection.AP) 
            or not controller.framesState(connection.AP)[0][slice]):
            connection.assignFrameSlice(0, slice)
            assigned += 1
            if assigned == numberOfSlices:
                break
    
    # Assign remaining slices in future frames
    frameIndex = 1
    while assigned < numberOfSlices:
        if frameIndex >= len(controller.framesState(connection.AP)):
            connection.assignFrameSlice(frameIndex, 0)
        else:
            for slice in range(connection.AP.slicesInFrame):
                if not controller.framesState(connection.AP)[frameIndex][slice]:
                    connection.assignFrameSlice(frameIndex, slice)
                    break
        assigned += 1
        frameIndex += 1
    
    return Controller.status.ALLOCATED, connection

# Setup simulation with 20x20x2.15m room
sim = Simulator(20.0, 20.0, 2.15, 10, 0.8)

# Add 4 VLeds in a grid pattern
for x, y in [(-7.5, -7.5), (-7.5, 7.5), (7.5, -7.5), (7.5, 7.5)]:
    vled = VLed(x, y, 2.15, 60, 60, 20, 70)
    vled.sliceTime = 0.2
    vled.slicesInFrame = 10
    vled.B = 0.5e5
    sim.scenario.addVLed(vled)

# Add RF fallback
rf = RF(0, 0, 0.85)
rf.sliceTime = 0.2
rf.slicesInFrame = 10
rf.B = 0.5e5
sim.scenario.addRF(rf)

# Configure simulation parameters
sim.set_allocation_algorithm(custom_allocation_algorithm)
sim.goalConnections = 60
sim.lambdaS = 1  # 1 arrival per time unit
sim.mu = 30  # Average service time
sim.upper_random_wait = 20
sim.lower_random_wait = 2
sim.lower_capacity_required = 1e5
sim.upper_capacity_required = 5e5

# Run simulation
sim.init()
sim.run()
```

## Testing

VLCSim has comprehensive test coverage (72%) with 166 tests.

### Run Tests

```bash
# All tests
pytest tests/

# With coverage
pytest tests/ --cov=vlcsim --cov-report=term

# Specific module
pytest tests/test_controller.py -v
pytest tests/test_simulator.py -v
pytest tests/test_scenario.py -v
```

### Coverage by Module

| Module                            | Coverage |
| --------------------------------- | -------- |
| `vlcsim/__init__.py`              | 100%     |
| `vlcsim/scene/__init__.py`        | 100%     |
| `vlcsim/scene/access_point.py`    | 88%      |
| `vlcsim/scene/vled.py`            | 88%      |
| `vlcsim/scene/rf.py`              | 88%      |
| `vlcsim/scene/receiver.py`        | 88%      |
| `vlcsim/scene/scenario.py`        | 88%      |
| `vlcsim/controller/__init__.py`   | 100%     |
| `vlcsim/controller/connection.py` | 71%      |
| `vlcsim/controller/controller.py` | 71%      |
| `vlcsim/simulator.py`             | 49%      |
| **Total**                         | **72%**  |

## API Reference

### Simulator Class

Main simulation engine for event-driven VLC simulations.

```python
sim = Simulator(length, width, height, nGrids, rho)
sim.init()              # Initialize simulation
sim.run()               # Execute simulation
sim.set_allocation_algorithm(func)  # Set custom allocator
sim.print_initial_info()  # Display scenario configuration
```

**Key Parameters:**
- `lambdaS`: Arrival rate (connections per time unit)
- `mu`: Service rate parameter
- `goalConnections`: Total connections to simulate
- `seedX, seedY, seedZ`: Random seeds for position generation
- `upper_random_wait, lower_random_wait`: Retry delay bounds (seconds)
- `upper_capacity_required, lower_capacity_required`: Capacity bounds (bps)

### Scenario Class

Room configuration with access points.

```python
scenario.addVLed(vled)   # Add VLC access point
scenario.addRF(rf)       # Add RF access point
scenario.capacityVled(receiver, vled)  # Calculate VLC capacity
scenario.capacityRf(receiver, rf)      # Calculate RF capacity
```

### Controller Class

Manages connections and resource allocation.

```python
controller.assignConnection(connection, time)
controller.numberOfActiveConnections(ap)
controller.framesState(ap)
controller.APPosition(ap)
controller.init()  # Initialize controller with APs
```

### Connection Class

Represents a data transmission session.

```python
connection.assignFrameSlice(frame, slice)
connection.numberOfSlicesNeeded(capacity, rate)
connection.nextSliceInAPWhenArriving(ap)
```

**Key Properties:**
- `id`: Connection identifier
- `receiver`: Receiver object
- `AP`: Assigned access point (VLed or RF)
- `allocated`: Allocation status (boolean)
- `capacityRequired`: Required data rate (bps)
- `frameSlice`: Assigned TDM slices

## Configuration Parameters

### VLed Parameters

```python
vled = VLed(x, y, z, nLedsX, nLedsY, ledPower, theta)
```

- `x, y, z`: Position coordinates (meters)
- `nLedsX, nLedsY`: LED array dimensions (rows x columns)
- `ledPower`: LED power (mW)
- `theta`: Semi-angle at half illumination (degrees)
- `sliceTime`: Time slice duration (seconds)
- `slicesInFrame`: Number of slices per frame
- `B`: Bandwidth (Hz)

### RF Parameters

```python
rf = RF(x, y, z, bf=5e6, pf=40, BERf=10e-5)
```

- `x, y, z`: Position coordinates (meters)
- `bf`: Bandwidth (Hz)
- `pf`: Transmission power (dBm)
- `BERf`: Bit Error Rate target
- `sliceTime`: Time slice duration (seconds)
- `slicesInFrame`: Number of slices per frame

### Receiver Parameters

```python
receiver = Receiver(x, y, z, aDet, ts, index, fov)
```

- `x, y, z`: Position coordinates (meters)
- `aDet`: Detector area (m²)
- `ts`: Optical filter transmittance
- `index`: Refractive index
- `fov`: Field of view (degrees)

## Contributing

Contributions are welcome! We follow Git-Flow workflow and Conventional Commits standards.

**Quick Start:**

1. Fork the repository on GitLab
2. Clone your fork and set up development environment
   ```bash
   git clone https://gitlab.com/YOUR_USERNAME/simvlc.git
   cd simvlc
   poetry install
   ```
3. Create a feature branch using Git-Flow
   ```bash
   git flow feature start feature-name
   ```
4. Make your changes with tests
5. Run tests and ensure they pass
   ```bash
   poetry run pytest tests/ --cov=vlcsim
   ```
6. Commit changes with descriptive messages (use [Conventional Commits](https://www.conventionalcommits.org/))
   ```bash
   git commit -m 'feat: add new feature description'
   ```
7. Finish the feature branch
   ```bash
   git flow feature finish feature-name
   ```
8. Push to your fork and create a Merge Request

**For detailed guidelines, see [CONTRIBUTING.md](CONTRIBUTING.md)**

Also see:
- [VERSIONING.md](VERSIONING.md) - Release workflow and Git-Flow process
- [CHANGELOG.md](CHANGELOG.md) - Project history and version changes

## Development

### Project Structure

```
vlcsim/
├── vlcsim/
│   ├── __init__.py         # Package initialization
│   ├── controller/         # Connection management and allocation module (modular structure)
│   │   ├── __init__.py       # Controller package exports
│   │   ├── connection.py     # Connection class (transmission session)
│   │   └── controller.py     # Controller class (resource allocation)
│   ├── scene/              # Physical infrastructure module (modular structure)
│   │   ├── __init__.py       # Scene package exports
│   │   ├── access_point.py   # AccessPoint base class
│   │   ├── vled.py           # VLed (Visible Light LED) class
│   │   ├── rf.py             # RF (Radio Frequency) class
│   │   ├── receiver.py       # Receiver device class
│   │   └── scenario.py       # Scenario environment class
│   └── simulator.py        # Event-driven simulation engine
├── tests/
│   ├── test_controller.py   # Controller tests (42 tests)
│   ├── test_simulator.py    # Simulator tests (40 tests)
│   ├── test_connection.py   # Connection tests (29 tests)
│   ├── test_scenario.py     # Scenario tests
│   ├── test_vled.py         # VLed tests
│   ├── test_rf.py           # RF tests
│   └── test_receiver.py     # Receiver tests
├── docs/                # Sphinx documentation
├── CHANGELOG.md         # Version history
├── VERSIONING.md        # Release workflow guide
├── README.md            # This file
├── pyproject.toml       # Poetry configuration
└── requirements.txt     # Pip fallback dependencies
```

### Setting Up Development Environment

```bash
# Clone repository
git clone https://gitlab.com/DaniloBorquez/simvlc.git
cd simvlc

# Install with Poetry (recommended)
poetry install

# Or with pip
pip install -e .

# Run tests
poetry run pytest tests/

# Run tests with coverage
poetry run pytest tests/ --cov=vlcsim --cov-report=html

# View coverage report
open htmlcov/index.html

# Build package (uses dynamic versioning from Git tags)
poetry build
```

### Release Process

This project uses **poetry-dynamic-versioning** for automatic version management from Git tags.
For detailed release instructions, see [VERSIONING.md](VERSIONING.md).

```bash
# Quick release workflow (Git-Flow)
git flow release start 0.5.0
# Update CHANGELOG.md
git flow release finish 0.5.0
git push origin --all && git push origin --tags
```

Releases to PyPI are automatically triggered when version tags are pushed to the `main` branch.

## License

This project is licensed under the MIT License - see the [LICENSE.md](LICENSE.md) file for details.

## Project Statistics

- **Lines of Code**: ~2,500
- **Test Coverage**: 72%
- **Number of Tests**: 166 passing
- **Python Version**: 3.9+
- **Modules**: 3 main modules (controller, scene, simulator)
- **Package Manager**: Poetry with dynamic versioning
- **Latest Release**: See [CHANGELOG.md](CHANGELOG.md)

## Acknowledgments

- Based on research in Visible Light Communication systems
- Event-driven simulation methodology
- TDM resource allocation strategies
- Hybrid VLC/RF communication systems

## Contact

For questions, issues, or contributions, please:
- Open an issue on GitLab: https://gitlab.com/DaniloBorquez/vlcsim/-/issues
- Submit a merge request
- Contact the maintainers

---

**Made with dedication for VLC research and simulation**
