# mhs5200

Python driver for MHS-5200A series DDS signal generators.

Control MHS-5200A dual-channel function generators via USB serial connection. Supports frequency, amplitude, waveform, sweep, and arbitrary waveform upload.

## Features

- Dual-channel control (frequency, amplitude, waveform, duty cycle, offset, phase)
- Sweep function (linear/logarithmic)
- Arbitrary waveform upload/download (2048 samples, 12-bit resolution)
- Built-in waveforms (sine, square, triangle, sawtooth)
- Automatic attenuation handling

## Supported Models

MHS-5200A series: MHS-5206A, MHS-5212A, MHS-5220A, MHS-5225A

## Installation

```bash
pip install mhs5200
```

## Quick Start

```python
from mhs5200 import MHS5200, Waveform

with MHS5200('/dev/ttyUSB0') as gen:  # Windows: 'COM3'
    print(gen.get_device_info())
    
    # CH1: 1kHz sine wave, 5Vpp
    gen.set_frequency(1, 1000)
    gen.set_amplitude(1, 5.0)
    gen.set_waveform(1, Waveform.SINE)
    gen.set_all_outputs(True)
```

## Examples

### Dual Channel with Phase Shift

```python
from mhs5200 import MHS5200, Waveform

with MHS5200('/dev/ttyUSB0') as gen:
    # CH1: 1kHz sine
    gen.set_frequency(1, 1000)
    gen.set_amplitude(1, 5.0)
    gen.set_waveform(1, Waveform.SINE)
    
    # CH2: 1kHz sine, 90° phase shift (quadrature)
    gen.set_frequency(2, 1000)
    gen.set_amplitude(2, 5.0)
    gen.set_waveform(2, Waveform.SINE)
    gen.set_phase(90)
    
    gen.set_all_outputs(True)
```

### Square Wave with Duty Cycle

```python
from mhs5200 import MHS5200, Waveform

with MHS5200('/dev/ttyUSB0') as gen:
    gen.set_frequency(1, 10000)
    gen.set_amplitude(1, 3.3)
    gen.set_waveform(1, Waveform.SQUARE)
    gen.set_duty_cycle(1, 25.0)  # 25%
    gen.set_all_outputs(True)
```

### Frequency Sweep

```python
from mhs5200 import MHS5200, SweepMode

with MHS5200('/dev/ttyUSB0') as gen:
    gen.configure_sweep(
        start_hz=100,
        stop_hz=10000,
        time_sec=10,
        mode=SweepMode.LINEAR
    )
    gen.sweep_on()
```

### Arbitrary Waveform

```python
import math
from mhs5200 import MHS5200, Waveform

def generate_waveform(num_samples=2048):
    """Sine wave with 3rd harmonic (30%)."""
    samples = []
    for i in range(num_samples):
        t = i / num_samples * 2 * math.pi
        value = math.sin(t) + 0.3 * math.sin(3 * t)
        sample = int(2048 + value / 1.3 * 2047)
        samples.append(max(0, min(4095, sample)))
    return samples

with MHS5200('/dev/ttyUSB0') as gen:
    waveform = generate_waveform()
    gen.upload_arbitrary_waveform(0, waveform)
    
    gen.set_waveform(1, Waveform.ARB00)
    gen.set_frequency(1, 1000)
    gen.set_amplitude(1, 3.0)
    gen.set_all_outputs(True)
```

## API Reference

### Device Information

| Method | Description |
|--------|-------------|
| `get_device_info()` | Returns dict with model and serial |
| `get_model()` | Returns model name |
| `get_serial_number()` | Returns serial number |

### Channel Control

| Method | Description |
|--------|-------------|
| `set_frequency(ch, hz)` | Set frequency (0.01 Hz resolution) |
| `get_frequency(ch)` | Get current frequency |
| `set_amplitude(ch, vpp)` | Set amplitude in Vpp |
| `get_amplitude(ch)` | Get current amplitude |
| `set_amplitude_auto(ch, vpp)` | Set amplitude with auto attenuation |
| `set_waveform(ch, waveform)` | Set waveform type |
| `get_waveform_name(ch)` | Get waveform name |
| `set_duty_cycle(ch, percent)` | Set duty cycle (0–99.9%) |
| `set_offset(ch, percent)` | Set DC offset (−120% to +120%) |
| `set_invert(ch, bool)` | Invert waveform |

### Common Control

| Method | Description |
|--------|-------------|
| `set_phase(degrees)` | Set phase (−180° to +180°) |
| `set_tracking(bool)` | Enable/disable channel tracking |

### Output Control

| Method | Description |
|--------|-------------|
| `set_all_outputs(bool)` | Enable/disable all outputs |
| `get_all_outputs()` | Get output state |

### Sweep

| Method | Description |
|--------|-------------|
| `configure_sweep(start, stop, time, mode, ch)` | Configure and start sweep |
| `sweep_on()` | Start sweep |
| `sweep_off()` | Stop sweep |
| `set_sweep_range(start_hz, stop_hz)` | Set frequency range |
| `set_sweep_time(seconds)` | Set sweep duration (1–600s) |
| `set_sweep_mode(mode)` | Set LINEAR or LOG mode |

### Arbitrary Waveform

| Method | Description |
|--------|-------------|
| `upload_arbitrary_waveform(slot, samples)` | Upload 2048 samples to slot 0–15 |
| `download_arbitrary_waveform(slot)` | Download waveform data |

### Enums

```python
# Waveform types
Waveform.SINE
Waveform.SQUARE
Waveform.TRIANGLE
Waveform.SAWTOOTH_UP
Waveform.SAWTOOTH_DOWN
Waveform.ARB00 – Waveform.ARB15

# Sweep modes
SweepMode.LINEAR
SweepMode.LOG

# Attenuation
Attenuation.ATT_0DB
Attenuation.ATT_20DB
```

## License

MIT License
