From 1c40f0ec7706f3e4c12e3d3ef8c79eeb9bac7eb6 Mon Sep 17 00:00:00 2001 From: Simon Humpohl <simon.humpohl@rwth-aachen.de> Date: Tue, 14 Nov 2017 15:40:04 +0100 Subject: [PATCH] Update pytabor to current tabor state --- pytabor/pytabor16.py | 201 +++++++++++++++++++++++++++++++++++-------- 1 file changed, 163 insertions(+), 38 deletions(-) diff --git a/pytabor/pytabor16.py b/pytabor/pytabor16.py index df033d4..e17932e 100644 --- a/pytabor/pytabor16.py +++ b/pytabor/pytabor16.py @@ -1,5 +1,33 @@ -'''Tabor-Electronics Utilities (for `pyvisa` 1.6 or above).''' +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# ========================================================================= +# Copyright (C) 2016 Tabor-Electronics Ltd <http://www.taborelec.com/> +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# ========================================================================= +''' +pyte16 -- control Tabor-Electronics instruments using `pyvisa` 1.6 or above. + +@author: Nadav +@date: 2016-11-23 +@license: GPL +@copyright: 2016 Tabor-Electronics Ltd. +@contact: <http://www.taborelec.com/> +''' +from __future__ import print_function +from builtins import input import sys import socket import ctypes @@ -10,11 +38,28 @@ import numpy as np from pyvisa import ResourceManager import pyvisa.constants as vc -__all__ = ['open_session', 'prompt_msg','make_bin_dat_header', 'get_visa_err_desc', 'write_raw_string', - 'write_raw_bin_dat', 'send_cmd', 'build_sine_wave', 'build_triangle_wave', 'build_square_wave', - 'download_binary_data', 'download_binary_file', 'download_arbcon_wav_file', 'download_segment_lengths', - 'download_sequencer_table', 'download_adv_seq_table', 'download_fast_pattern_table', - 'download_linear_pattern_table', 'make_combined_wave', 'PyteException'] +__version__ = '1.0.1' +__revision__ = '$Rev: 4238 $' +__docformat__ = 'reStructuredText' + +__all__ = [ + 'open_session', + 'send_cmd', + 'download_binary_data', + 'download_binary_file', + 'download_binary_file', + 'download_arbcon_wav_file', + 'download_segment_lengths', + 'download_sequencer_table', + 'download_adv_seq_table', + 'download_fast_pattern_table', + 'download_linear_pattern_table', + 'build_sine_wave', + 'build_triangle_wave', + 'build_square_wave', + 'add_markers', + 'make_combined_wave', + 'PyteException'] class PyteException(Exception): @@ -209,7 +254,7 @@ def _select_visa_rsc_name(rsc_manager=None, title=None, interface_name=None): if len(rsc_descs) != num_rscs: rsc_descs = ["" for n in range(num_rscs)] # get resources descriptions: - for n, name in zip(list(range(num_rscs)), rsc_names): + for n, name in zip(range(num_rscs), rsc_names): vi = None try: vi =rsc_manager.open_resource(name) @@ -227,7 +272,7 @@ def _select_visa_rsc_name(rsc_manager=None, title=None, interface_name=None): pass print("Please choose one of the available devices:") - for n, name, desc in zip(list(range(num_rscs)), rsc_names, rsc_descs): + for n, name, desc in zip(range(num_rscs), rsc_names, rsc_descs): print(" {0:d}. {1} ({2})".format(n+1, desc, name)) print(" {0:d}. Back to main menu".format(num_rscs+1)) msg = "Please enter your choice [{0:d}:{1:d}]: ".format(1, num_rscs+1) @@ -244,10 +289,10 @@ def _select_visa_rsc_name(rsc_manager=None, title=None, interface_name=None): else: selected_rsc_name = rsc_names[choice - 1] break - - return selected_rsc_name -def _init_vi_inst(vi, timeout_msec=10000, read_buff_size_bytes=4096, write_buff_size_bytes=4096): + return selected_rsc_name + +def _init_vi_inst(vi, timeout_msec=30000, read_buff_size_bytes=4096, write_buff_size_bytes=4096): '''Initialize the given Instrument VISA Session :param vi: `pyvisa` instrument. @@ -265,12 +310,16 @@ def _init_vi_inst(vi, timeout_msec=10000, read_buff_size_bytes=4096, write_buff_ vi.read_termination = '\n' vi.write_termination = '\n' intf_type = vi.get_visa_attribute(vc.VI_ATTR_INTF_TYPE) - if intf_type in (vc.VI_INTF_USB, vc.VI_INTF_GPIB, vc.VI_INTF_TCPIP): + if intf_type in (vc.VI_INTF_USB, vc.VI_INTF_GPIB, vc.VI_INTF_TCPIP, vc.VI_INTF_ASRL): vi.set_visa_attribute(vc.VI_ATTR_WR_BUF_OPER_MODE, vc.VI_FLUSH_ON_ACCESS) vi.set_visa_attribute(vc.VI_ATTR_RD_BUF_OPER_MODE, vc.VI_FLUSH_ON_ACCESS) if intf_type == vc.VI_INTF_TCPIP: - vi.set_visa_attribute(vc.VI_ATTR_TERMCHAR_EN, vc.VI_TRUE) # vc.VI_FALSE - vi.clear() + vi.set_visa_attribute(vc.VI_ATTR_TERMCHAR_EN, vc.VI_TRUE) # vc.VI_FALSE + elif intf_type == vc.VI_INTF_ASRL: + vi.set_visa_attribute(vc.VI_ATTR_ASRL_BAUD, 115200) + vi.set_visa_attribute(vc.VI_ATTR_ASRL_END_OUT, 0) + vi.set_visa_attribute(vc.VI_ATTR_ASRL_END_IN, 2) + vi.clear() def open_session(resource_name = None, title_msg = None, vi_rsc_mgr = None, extra_init=True): '''Open VISA Session (optionally prompt for resource name). @@ -446,11 +495,19 @@ def send_cmd(vi, cmd_str, paranoia_level=1): else: ask_str = ':SYST:ERR?' syst_err = vi.ask(ask_str) - if not syst_err.startswith('0'): + try: + errnb = int(syst_err.split(',')[0]) + except: + errnb = -1 + if errnb != 0: syst_err = syst_err.rstrip() - wrn_msg = 'ERR: "{0}" after CMD: "{1}"'.format(cmd_str, syst_err) - warnings.warn(wrn_msg) + wrn_msg = 'ERR: "{0}" after CMD: "{1}"'.format(syst_err, cmd_str) _ = vi.ask('*CLS; *OPC?') # clear the error-list + if paranoia_level >= 3: + raise NameError(wrn_msg) + else: + warnings.warn(wrn_msg) + else: vi.write(cmd_str) @@ -462,10 +519,10 @@ def _pre_download_binary_data(vi, bin_dat_size=None): :returns: the max write-chunk size (in bytes) and the original time-out (in msec) ''' orig_timeout = vi.timeout - max_chunk_size = 4096 - - try: - max_chunk_size = vi.write_buff_size if hasattr(vi, 'write_buff_size') else max_chunk_size + + max_chunk_size = vi.write_buff_size if hasattr(vi, 'write_buff_size') else 4096 + + try: intf_type = vi.get_visa_attribute(vc.VI_ATTR_INTF_TYPE) if intf_type == vc.VI_INTF_GPIB: _ = vi.write("*OPC?") @@ -562,15 +619,21 @@ def download_binary_data(vi, pref, bin_dat, dat_size, paranoia_level=1): if not syst_err.startswith('0'): syst_err = syst_err.rstrip() wrn_msg = 'ERR: "{0}" after sending binary data (pref="{1}", dat_size={2})'.format(syst_err, pref, dat_size) - warnings.warn(wrn_msg) _ = vi.ask('*CLS; *OPC?') # clear the error-list - + if paranoia_level >= 3: + raise NameError(wrn_msg) + else: + warnings.warn(wrn_msg) + except: if ret_count >= 0: ret_count = -1 - wrn_msg = 'Error in download_binary_data(pref="{0}", dat_size={1}): \n{2}'.format(pref, dat_size, sys.exc_info()) - warnings.warn(wrn_msg) - + wrn_msg = 'Error in download_binary_data(pref="{0}", dat_size={1}): \n{2}'.format(pref, dat_size, sys.exc_info()) + if paranoia_level >= 3: + raise NameError(wrn_msg) + else: + warnings.warn(wrn_msg) + return ret_count def download_binary_file(vi, pref, file_path, offset=0, data_size=None, paranoia_level=1): @@ -817,15 +880,18 @@ def download_sequencer_table(vi, seq_table, pref=':SEQ:DATA', paranoia_level=1): >>> pytabor.download_sequencer_table(vi, sequencer_table) ''' - - tbl_len = len(seq_table) + try: + tbl_len = len(seq_table) + except: + seq_table = list(seq_table) + tbl_len = len(seq_table) s = struct.Struct('< L H B x') s_size = s.size m = np.empty(s_size * tbl_len, dtype='uint8') for n in range(tbl_len): repeats, seg_nb, jump_flag = seq_table[n] - s.pack_into(m, n * s_size, int(repeats), int(seg_nb), int(jump_flag)) - + s.pack_into(m, n * s_size, np.uint32(repeats), np.uint16(seg_nb), np.uint8(jump_flag)) + return download_binary_data(vi, pref, m, m.nbytes, paranoia_level=paranoia_level) def download_adv_seq_table(vi, adv_seq_table, pref=':ASEQ:DATA', paranoia_level=1): @@ -850,15 +916,18 @@ def download_adv_seq_table(vi, adv_seq_table, pref=':ASEQ:DATA', paranoia_level= >>> # Download it to instrument >>> pytabor.download_adv_seq_table(vi, adv_sequencer_table) ''' - - tbl_len = len(adv_seq_table) + try: + tbl_len = len(adv_seq_table) + except: + adv_seq_table = list(adv_seq_table) + tbl_len = len(adv_seq_table) s = struct.Struct('< L H B x') s_size = s.size m = np.empty(s_size * tbl_len, dtype='uint8') for n in range(tbl_len): repeats, seq_nb, jump_flag = adv_seq_table[n] - s.pack_into(m, n * s_size, int(repeats), int(seq_nb), int(jump_flag)) - + s.pack_into(m, n * s_size, np.uint32(repeats), np.uint16(seq_nb), np.uint8(jump_flag)) + return download_binary_data(vi, pref, m, m.nbytes, paranoia_level=paranoia_level) def download_fast_pattern_table(vi, patt_table, pref=':PATT:COMP:FAST:DATA', paranoia_level=1): @@ -901,8 +970,11 @@ def download_fast_pattern_table(vi, patt_table, pref=':PATT:COMP:FAST:DATA', par >>> >>> vi.close() ''' - - tbl_len = len(patt_table) + try: + tbl_len = len(patt_table) + except: + patt_table = list(patt_table) + tbl_len = len(patt_table) s = struct.Struct('< f d') s_size = s.size m = np.empty(s_size * tbl_len, dtype='uint8') @@ -960,8 +1032,11 @@ def download_linear_pattern_table(vi, patt_table, start_level, pref=':PATT:COMP: >>> >>> vi.close() ''' - - tbl_len = len(patt_table) + try: + tbl_len = len(patt_table) + except: + patt_table = list(patt_table) + tbl_len = len(patt_table) s = struct.Struct('< f d') s_size = s.size m = np.empty(s_size * tbl_len, dtype='uint8') @@ -1097,6 +1172,56 @@ def build_square_wave(cycle_len, num_cycles=1, duty_cycle=50.0, phase_degree=0, return wav +def add_markers(dat_buff, marker_pos, marker_width, marker_bit1, marker_bit2, dat_offs=0, dat_len=None): + """Add markers bits to the wave-data in the given buffer. + + Note that in case of 4-channels devices, the markers bits + are both added to the 1st channel of each channels-pair. + + IMPORTANT: This function currently fits only 4-channels devices (WX2184 / WX1284). + + :param dat_buff: `numpy` array containing the wave-data (data-type='uint16') + :param marker_pos: the marker start-position within the wave-data (in wave-points) + :param marker_width: the marker width (in wave-points). + :param marker_bit1: the value of 1st marker's bit (zero or one) + :param marker_bit2: the value of 2nd marker's bit (zero or one) + :param dat_offs: the offset of the wave-data within the data-buffer (default: 0). + :param dat_len: the length of the actual wave-data (default: the length of `dat_buff`). + """ + + shift_pts = 12 + + if dat_len is None: + dat_len = len(dat_buff) - dat_offs + + if len(dat_buff) > 0 and dat_len > 0 and marker_width > 0: + + marker_bits = 0 + if marker_bit1: + marker_bits |= 0x4000 + if marker_bit2: + marker_bits |= 0x8000 + + assert(marker_pos % 2 == 0) + assert(marker_width % 2 == 0) + assert(dat_len % 16 == 0 and dat_len >= 16) + + seg_pos = (marker_pos + shift_pts) % dat_len + seg_pos = (seg_pos//16)*16 + 8 + (seg_pos%16)//2 + + while marker_width > 0: + if seg_pos >= dat_len: + seg_pos = 8 + + buf_index = (dat_offs + seg_pos) % len(dat_buff) + dat_buff[buf_index] &= 0x3fff + dat_buff[buf_index] |= marker_bits + + marker_width -= 2 + seg_pos += 1 + if seg_pos % 16 == 0: + seg_pos += 8 + def make_combined_wave(wav1, wav2, dest_array, dest_array_offset=0, add_idle_pts=False, quantum=16): '''Make 2-channels combined wave from the 2 given waves -- GitLab