vlcSim 0.4.1
Python Package of Event-Oriented Simulation for visible light communication
License: MIT
Requires Python: <4.0,>=3.9
Keywords: vlc, visible light communication, simulation, event-oriented

# VLCSim - Visible Light Communication Simulator

![Python Version](https://img.shields.io/badge/python-3.8+-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-VLCSim-orange.svg)

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 Source

```bash
git clone https://gitlab.com/DaniloBorquez/vlcsim.git
cd vlcsim
pip install -e .
```

### Dependencies

```bash
pip install -r requirements.txt
```

Main dependencies:
- `numpy`: Numerical computations and random number generation
- `pytest`: Testing framework (development)
- `pytest-cov`: Coverage reporting (development)

## 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.py` | 88% |
| `vlcsim/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! Please follow these steps:

1. Fork the repository
2. Create feature branch (`git flow feature start feature-name`)
3. Make your changes
4. Run tests (`pytest tests/`)
5. Commit changes (`git commit -m 'Add feature'`)
6. Push branch (`git push origin feature/feature-name`)
7. Open Pull Request

## Development

### Project Structure

```
vlcsim/
├── vlcsim/
│   ├── __init__.py      # Package initialization
│   ├── controller.py    # Connection management and allocation
│   ├── scene.py         # Scenario, VLed, RF, Receiver classes
│   └── 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/                # Documentation
├── CHANGELOG.md         # Version history
├── README.md            # This file
└── requirements.txt     # Dependencies
```

### Running Development Environment

```bash
# Install in development mode
pip install -e .

# Install development dependencies
pip install -r requirements.txt

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

# View coverage report
open htmlcov/index.html
```

## 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.8+
- **Modules**: 3 main modules (controller, scene, simulator)
- **Last Updated**: November 27, 2025

## 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**