Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: m-labs/artiq
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: db5957a7e786
Choose a base ref
...
head repository: m-labs/artiq
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 161025e7df74
Choose a head ref
  • 3 commits
  • 7 files changed
  • 2 contributors

Commits on Dec 19, 2016

  1. Unverified

    This commit is not signed, but one or more authors requires that any commit attributed to them is signed.
    Copy the full SHA
    baac555 View commit details
  2. korad_ka3005p: cleanup

    sbourdeauducq committed Dec 19, 2016

    Unverified

    This commit is not signed, but one or more authors requires that any commit attributed to them is signed.
    Copy the full SHA
    d55f2bd View commit details
  3. Copy the full SHA
    161025e View commit details
Empty file.
147 changes: 147 additions & 0 deletions artiq/devices/korad_ka3005p/driver.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
# Written by Joe Britton, 2016

import logging
import asyncio
import asyncserial

logger = logging.getLogger(__name__)


class UnexpectedResponse(Exception):
pass


class KoradKA3005P:
"""The Korad KA3005P is a 1-channel programmable power supply
(0-30V/0-5A) with both USB/serial and RS232 connectivity.
All amplitudes are in volts.
All currents are in amperes.
"""

# Serial interface gleaned from the following.
# https://github.com/starforgelabs/py-korad-serial
# https://sigrok.org/wiki/Korad_KAxxxxP_series

def __init__(self, serial_dev):
if serial_dev is None:
self.simulation = True
else:
self.simulation = False
self.port = asyncserial.AsyncSerial(serial_dev, baudrate=9600)

def close(self):
"""Close the serial port."""
if not self.simulation:
self.port.close()

async def _ser_read(self, fixed_length=None):
""" strings returned by firmware are zero-terminated or fixed length
"""
c = (await self.port.read(1)).decode()
r = c
while len(c) > 0 and ord(c) != 0 and not len(r) == fixed_length:
c = (await self.port.read(1)).decode()
r += c
logger.debug("_read %s: ", r)
return r

async def _ser_write(self, cmd):
logger.debug("_write %s: ", cmd)
await asyncio.sleep(0.1)
await self.port.write(cmd.encode('ascii'))

async def setup(self):
"""Configure in known state."""
await self.set_output(False)
await self.set_v(0)
await self.set_ovp(False)
await self.set_i(0)
await self.set_ocp(False)

async def get_id(self):
"""Request identification from device.
"""
if self.simulation:
return "KORADKA3005PV2.0"
await self._ser_write("*IDN?")
return await self._ser_read()

async def set_output(self, b):
"""Enable/disable the power output.
"""
if b:
await self._ser_write("OUT1")
else:
await self._ser_write("OUT0")

async def set_v(self, v):
"""Set the maximum output voltage."""
await self._ser_write("VSET1:{0:05.2f}".format(v))

async def get_v(self):
"""Request the voltage as set by the user."""
await self._ser_write("VSET1?")
return float(await self._ser_read(fixed_length=5))

async def measure_v(self):
"""Request the actual voltage output."""
await self._ser_write("VOUT1?")
return float(await self._ser_read(fixed_length=5))

async def set_ovp(self, b):
"""Enable/disable the "Over Voltage Protection", the PS will switch off the
output when the voltage rises above the actual level."""
if b:
await self._ser_write("OVP1")
else:
await self._ser_write("OVP0")

async def set_i(self, v):
"""Set the maximum output current."""
await self._ser_write("ISET1:{0:05.3f}".format(v))

async def get_i(self):
"""Request the current as set by the user. """

# ISET1? replies with a sixth byte on many models (all?)
# which is the sixth character from *IDN?
# reply if *IDN? was queried before (during same power cycle).
# This byte is read and discarded.
await self._ser_write("ISET1?")
r = await self._ser_read(fixed_length=5)
if r[0] == "K":
r = r[1:-1]
return float(r)

async def measure_i(self):
"""Request the actual output current."""
await self._ser_write("IOUT1?")
r = await self._ser_read(fixed_length=6)
if r[0] == "K":
r = r[1:-1]
return float(r)

async def set_ocp(self, b):
"""Enable/disable the "Over Current Protection", the PS will switch off
the output when the current rises above the actual level."""
if b:
await self._ser_write("OCP1")
else:
await self._ser_write("OCP0")

async def ping(self):
"""Check if device is responding."""
if self.simulation:
return True
try:
id = await self.get_id()
except asyncio.CancelledError:
raise
except:
return False
if id == "KORADKA3005PV2.0":
logger.debug("ping successful")
return True
else:
return False
54 changes: 54 additions & 0 deletions artiq/frontend/korad_ka3005p_controller.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
#!/usr/bin/env python3.5

# Written by Joe Britton, 2016

import argparse
import logging
import sys
import asyncio
import os

from artiq.devices.korad_ka3005p.driver import KoradKA3005P
from artiq.protocols.pc_rpc import simple_server_loop
from artiq.tools import *


logger = logging.getLogger(__name__)


def get_argparser():
parser = argparse.ArgumentParser(
description="ARTIQ controller for the Korad KA3005P programmable DC power supply")
simple_network_args(parser, 3256)
parser.add_argument(
"-d", "--device", default=None,
help="serial port.")
parser.add_argument(
"--simulation", action="store_true",
help="Put the driver in simulation mode, even if --device is used.")
verbosity_args(parser)
return parser


def main():
args = get_argparser().parse_args()
init_logger(args)

if os.name == "nt":
asyncio.set_event_loop(asyncio.ProactorEventLoop())

if not args.simulation and args.device is None:
print("You need to specify either --simulation or -d/--device "
"argument. Use --help for more information.")
sys.exit(1)

dev = KoradKA3005P(args.device if not args.simulation else None)
asyncio.get_event_loop().run_until_complete(dev.setup())
try:
simple_server_loop(
{"korad_ka3005p": dev}, bind_address_from_args(args), args.port)
finally:
dev.close()

if __name__ == "__main__":
main()
38 changes: 38 additions & 0 deletions artiq/test/test_korad_ka3005p.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import sys
import unittest

from artiq.test.hardware_testbench import GenericControllerCase, ControllerCase


class GenericKoradKA3005PTest:
def test_parameters_readback(self):

# check device ID baked into firmware
ids = self.driver.get_id()
self.assertEqual(ids, "KORADKA3005PV2.0")


class TestKoradKA3005P(GenericKoradKA3005PTest, ControllerCase):
def setUp(self):
ControllerCase.setUp(self)
self.start_controller("koradka3005p")
self.driver = self.device_mgr.get("koradka3005p")


class TestKoradKA3005P(GenericKoradKA3005PTest, GenericControllerCase):
def get_device_db(self):
return {
"korad_ka3005p": {
"type": "controller",
"host": "::1",
"port": 3256,
"command": (sys.executable.replace("\\", "\\\\")
+ " -m artiq.frontend.korad_ka3005p_controller "
+ "-p {port} --simulation")
}
}

def setUp(self):
GenericControllerCase.setUp(self)
self.start_controller("korad_ka3005p")
self.driver = self.device_mgr.get("korad_ka3005p")
3 changes: 3 additions & 0 deletions doc/manual/default_network_ports.rst
Original file line number Diff line number Diff line change
@@ -30,3 +30,6 @@ Default network ports
+--------------------------+--------------+
| Thorlabs T-Cube | 3255 |
+--------------------------+--------------+
| Korad KA3005P | 3256 |
-------------------------------------------

16 changes: 16 additions & 0 deletions doc/manual/ndsp_reference.rst
Original file line number Diff line number Diff line change
@@ -69,6 +69,22 @@ You can choose the LDA model with the ``-P`` parameter. The default is LDA-102.
:ref: artiq.frontend.lda_controller.get_argparser
:prog: lda_controller

Korad KA3005P
-------------

Driver
++++++

.. automodule:: artiq.devices.korad_ka3005p.driver
:members:

Controller
++++++++++

.. argparse::
:ref: artiq.frontend.korad_ka3005p_controller.get_argparser
:prog: korad_ka3005p_controller

Novatech 409B
-------------

1 change: 1 addition & 0 deletions setup.py
Original file line number Diff line number Diff line change
@@ -36,6 +36,7 @@
"artiq_flash=artiq.frontend.artiq_flash:main",
"lda_controller=artiq.frontend.lda_controller:main",
"novatech409b_controller=artiq.frontend.novatech409b_controller:main",
"korad_ka3005p_controller=artiq.frontend.korad_ka3005p_controller:main",
"pdq2_client=artiq.frontend.pdq2_client:main",
"pdq2_controller=artiq.frontend.pdq2_controller:main",
"thorlabs_tcube_controller=artiq.frontend.thorlabs_tcube_controller:main",