Skip to content
Merged
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
28 changes: 28 additions & 0 deletions doc/examples/minghe/ex_minghe_mhs5200.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#!/usr/bin/python
from instruments.minghe import MHS5200
import quantities as pq

mhs = MHS5200.open_serial(vid=6790, pid=29987, baud=57600)
print(mhs.serial_number)
mhs.channel[0].frequency = 3000000*pq.Hz
print(mhs.channel[0].frequency)
mhs.channel[0].function = MHS5200.Function.sawtooth_down
print(mhs.channel[0].function)
mhs.channel[0].amplitude = 9.0*pq.V
print(mhs.channel[0].amplitude)
mhs.channel[0].offset = -0.5
print(mhs.channel[0].offset)
mhs.channel[0].phase = 90
print(mhs.channel[0].phase)

mhs.channel[1].frequency = 2000000*pq.Hz
print(mhs.channel[1].frequency)
mhs.channel[1].function = MHS5200.Function.square
print(mhs.channel[1].function)
mhs.channel[1].amplitude = 2.0*pq.V
print(mhs.channel[1].amplitude)
mhs.channel[1].offset = 0.0
print(mhs.channel[1].offset)
mhs.channel[1].phase = 15
print(mhs.channel[1].phase)

15 changes: 15 additions & 0 deletions doc/source/apiref/minghe.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
..
TODO: put documentation license header here.

.. currentmodule:: instruments.minghe

======
Minghe
======

:class:`MHS5200` Function Generator
===================================

.. autoclass:: MHS5200
:members:
:undoc-members:
1 change: 1 addition & 0 deletions instruments/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
from . import hp
from . import keithley
from . import lakeshore
from . import minghe
from . import newport
from . import oxford
from . import phasematrix
Expand Down
7 changes: 7 additions & 0 deletions instruments/minghe/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Module containing MingHe instruments
"""
from __future__ import absolute_import
from .mhs5200a import MHS5200
235 changes: 235 additions & 0 deletions instruments/minghe/mhs5200a.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,235 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Provides the support for the MingHe low-cost function generator.

Class originally contributed by Catherine Holloway.
"""

# IMPORTS #####################################################################

from __future__ import absolute_import
from __future__ import division

from builtins import range
from enum import Enum

import quantities as pq
from instruments.abstract_instruments import FunctionGenerator
from instruments.util_fns import ProxyList, assume_units

# CLASSES #####################################################################


class MHS5200(FunctionGenerator):
"""
The MHS5200 is a low-cost, 2 channel function generator.

There is no user manual, but Al Williams has reverse-engineered the
communications protocol:
https://github.com/wd5gnr/mhs5200a/blob/master/MHS5200AProtocol.pdf
"""
def __init__(self, filelike):
super(MHS5200, self).__init__(filelike)
self._channel_count = 2
self.terminator = "\r\n"

def _ack_expected(self, msg=""):
if msg.find(":r") == 0:
return None
# most commands res
return "ok"

# INNER CLASSES #

class Channel(FunctionGenerator.Channel):
"""
Class representing a channel on the MHS52000.
"""
# pylint: disable=protected-access

__CHANNEL_NAMES = {
1: '1',
2: '2'
}

def __init__(self, mhs, idx):
self._mhs = mhs
super(MHS5200.Channel, self).__init__(parent=mhs, name=idx)
# Use zero-based indexing for the external API, but one-based
# for talking to the instrument.
self._idx = idx + 1
self._chan = self.__CHANNEL_NAMES[self._idx]
self._count = 0

def _get_amplitude_(self):
query = ":r{0}a".format(self._chan)
response = self._mhs.query(query)
return float(response.replace(query, ""))/100.0, self._mhs.VoltageMode.rms

def _set_amplitude_(self, magnitude, units):
if units == self._mhs.VoltageMode.peak_to_peak or \
units == self._mhs.VoltageMode.rms:
magnitude = assume_units(magnitude, "V").rescale(pq.V).magnitude
elif units == self._mhs.VoltageMode.dBm:
raise NotImplementedError("Decibel units are not supported.")
magnitude *= 100
query = ":s{0}a{1}".format(self._chan, int(magnitude))
self._mhs.sendcmd(query)

@property
def duty_cycle(self):
"""
Gets/Sets the duty cycle of this channel.

:units: A fraction
:type: `~quantities.Quantity`
"""
query = ":r{0}d".format(self._chan)
response = self._mhs.query(query)
duty = float(response.replace(query, ""))/10.0
return duty

@duty_cycle.setter
def duty_cycle(self, new_val):
query = ":s{0}d{1}".format(self._chan, int(100.0*new_val))
self._mhs.sendcmd(query)

@property
def enable(self):
"""
Gets/Sets the enable state of this channel.

:param newval: the enable state
:type: `bool`
"""
query = ":r{0}b".format(self._chan)
return int(self._mhs.query(query).replace(query, "").
replace("\r", ""))

@enable.setter
def enable(self, newval):
query = ":s{0}b{1}".format(self._chan, int(newval))
self._mhs.sendcmd(query)

@property
def frequency(self):
"""
Gets/Sets the frequency of this channel.

:units: As specified (if a `~quantities.Quantity`) or assumed to be
of units hertz.
:type: `~quantities.Quantity`
"""
query = ":r{0}f".format(self._chan)
response = self._mhs.query(query)
freq = float(response.replace(query, ""))*pq.Hz
return freq/100.0

@frequency.setter
def frequency(self, new_val):
new_val = assume_units(new_val, pq.Hz).rescale(pq.Hz).\
magnitude*100.0
query = ":s{0}f{1}".format(self._chan, int(new_val))
self._mhs.sendcmd(query)

@property
def offset(self):
"""
Gets/Sets the offset of this channel.

:param new_val: The fraction of the duty cycle to offset the
function by.
:type: `float`
"""
# need to convert
query = ":r{0}o".format(self._chan)
response = self._mhs.query(query)
return int(response.replace(query, ""))/100.0-1.20

@offset.setter
def offset(self, new_val):
new_val = int(new_val*100)+120
query = ":s{0}o{1}".format(self._chan, new_val)
self._mhs.sendcmd(query)

@property
def phase(self):
"""
Gets/Sets the phase of this channel.

:units: As specified (if a `~quantities.Quantity`) or assumed to be
of degrees.
:type: `~quantities.Quantity`
"""
# need to convert
query = ":r{0}p".format(self._chan)
response = self._mhs.query(query)
return int(response.replace(query, ""))*pq.deg

@phase.setter
def phase(self, new_val):
new_val = assume_units(new_val, pq.deg).rescale("deg").magnitude
query = ":s{0}p{1}".format(self._chan, int(new_val))
self._mhs.sendcmd(query)

@property
def function(self):
"""
Gets/Sets the wave type of this channel.

:type: `MHS5200.Function`
"""
query = ":r{0}w".format(self._chan)
response = self._mhs.query(query).replace(query, "")
return self._mhs.Function(int(response))

@function.setter
def function(self, new_val):
query = ":s{0}w{1}".format(self._chan,
self._mhs.Function(new_val).value)
self._mhs.sendcmd(query)

class Function(Enum):
"""
Enum containing valid wave modes for
"""
sine = 0
square = 1
triangular = 2
sawtooth_up = 3
sawtooth_down = 4

@property
def channel(self):
"""
Gets a specific channel object. The desired channel is specified like
one would access a list.

For instance, this would print the counts of the first channel::

>>> mhs = ik.minghe.MHS5200.open_serial(vid=1027, pid=24577,
baud=19200, timeout=1)
>>> print(mhs.channel[0].frequency)

:rtype: `list`[`MHS5200.Channel`]
"""
return ProxyList(self, MHS5200.Channel, range(self._channel_count))

@property
def serial_number(self):
"""
Get the serial number, as an int

:rtype: int
"""
query = ":r0c"
response = self.query(query)
response = response.replace(query, "").replace("\r", "")
return response

def _get_amplitude_(self):
raise NotImplementedError()

def _set_amplitude_(self, magnitude, units):
raise NotImplementedError()
Loading