• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2020 Google Inc. All Rights Reserved.
2# Author: oelayach@google.com
3
4import pyvisa
5import time
6from acts import logger
7
8class KeysightChamber(object):
9    """Base class implementation for signal generators.
10
11    Base class provides functions whose implementation is shared by all
12    chambers.
13    """
14    CHAMBER_SLEEP = 10
15
16    VISA_LOCATION = '/opt/keysight/iolibs/libktvisa32.so'
17
18    def __init__(self, config):
19        self.config = config
20        self.log = logger.create_tagged_trace_logger("{}{}".format(
21            self.config['brand'], self.config['model']))
22        self.chamber_resource = pyvisa.ResourceManager(self.VISA_LOCATION)
23        self.chamber_inst = self.chamber_resource.open_resource(
24            'TCPIP0::{}::{}::INSTR'.format(self.config['ip_address'],
25                                       self.config['hislip_interface']))
26        self.chamber_inst.timeout = 200000
27        self.chamber_inst.write_termination = '\n'
28        self.chamber_inst.read_termination = '\n'
29
30        self.id_check(self.config)
31        self.current_azim = 0
32        self.current_roll = 0
33        if self.config.get('reset_home', True):
34            self.find_chamber_home()
35            self.move_theta_phi_abs(self.config['chamber_home']['theta'],
36                                    self.config['chamber_home']['phi'])
37            self.set_new_home_position()
38        else:
39            self.config['chamber_home'] = {'phi': 0, 'theta': 0}
40            self.log.warning(
41                'Reset home set to false. Assumed [0,0]. Chamber angles may not be as expected.'
42            )
43        self.preset_orientations = self.config['preset_orientations']
44
45    def id_check(self, config):
46        """ Checks Chamber ID."""
47        self.log.info("ID Check Successful.")
48        self.log.info(self.chamber_inst.query("*IDN?"))
49
50    def reset(self):
51        self.reset_phi_theta()
52
53    def disconnect(self):
54        if self.config.get('reset_home', True):
55            self.reset_phi_theta()
56        self.chamber_inst.close()
57        self.chamber_resource.close()
58
59    def find_chamber_home(self):
60        self.chamber_inst.write(f"POS:BOR:INIT")
61        self.set_new_home_position()
62        self.wait_for_move_end()
63
64    def set_new_home_position(self):
65        self.chamber_inst.write(f"POS:ZERO:RES")
66
67    def get_phi(self):
68        return self.current_azim
69
70    def get_theta(self):
71        return self.current_roll
72
73    def get_pattern_sweep_limits(self):
74        return {
75            "pattern_phi_start": -self.config['chamber_home']['phi'],
76            "pattern_phi_stop": 165 - self.config['chamber_home']['phi'],
77            "pattern_theta_start": -self.config['chamber_home']['theta'],
78            "pattern_theta_stop": 360 - self.config['chamber_home']['theta'],
79        }
80
81    def move_phi_abs(self, phi):
82        self.log.info("Moving to Phi={}".format(phi))
83        self.move_to_azim_roll(phi, self.current_roll)
84
85    def move_theta_abs(self, theta):
86        self.log.info("Moving to Theta={}".format(theta))
87        self.move_to_azim_roll(self.current_azim, theta)
88
89    def move_theta_phi_abs(self, theta, phi):
90        self.log.info("Moving to Theta={}, Phi={}".format(theta, phi))
91        self.move_to_azim_roll(phi, theta)
92
93    def move_theta_phi_abs(self, theta, phi):
94        self.log.info("Moving chamber to [{}, {}]".format(theta, phi))
95        self.move_to_azim_roll(phi, theta)
96
97    def move_phi_rel(self, phi):
98        self.log.info("Moving Phi by {} degrees".format(phi))
99        self.move_to_azim_roll(self.current_azim + phi, self.current_roll)
100
101    def move_theta_rel(self, theta):
102        self.log.info("Moving Theta by {} degrees".format(theta))
103        self.move_to_azim_roll(self.current_azim, self.current_roll + theta)
104
105    def move_feed_roll(self, roll):
106        self.log.info("Moving feed roll to {} degrees".format(roll))
107        self.chamber_inst.write(f"POS:MOVE:ROLL:FEED {roll}")
108        self.chamber_inst.write("POS:MOVE:INIT")
109        self.wait_for_move_end()
110        self.current_feed_roll = self.chamber_inst.query("POS:MOVE:ROLL:FEED?")
111
112    def reset_phi(self):
113        self.log.info("Resetting Phi.")
114        self.move_to_azim_roll(0, self.current_roll)
115        self.phi = 0
116
117    def reset_theta(self):
118        self.log.info("Resetting Theta.")
119        self.move_to_azim_roll(self.current_azim, 0)
120        self.theta = 0
121
122    def reset_phi_theta(self):
123        """ Resets signal generator."""
124        self.log.info("Resetting to home.")
125        self.chamber_inst.write(f"POS:ZERO:GOTO")
126        self.wait_for_move_end()
127
128    # Keysight-provided functions
129    def wait_for_move_end(self):
130        moving_bitmask = 4
131        while True:
132            stat = int(self.chamber_inst.query("STAT:OPER:COND?"))
133            if (stat & moving_bitmask) == 0:
134                return
135            time.sleep(0.25)
136
137    def wait_for_sweep_end(self):
138        sweeping_bitmask = 16
139        while True:
140            stat = int(self.chamber_inst.query("STAT:OPER:COND?"))
141            if (stat & sweeping_bitmask) == 0:
142                return
143            time.sleep(0.25)
144
145    def move_to_azim_roll(self, azim, roll):
146        self.chamber_inst.write(f"POS:MOVE:AZIM {azim};ROLL {roll}")
147        self.chamber_inst.write("POS:MOVE:INIT")
148        self.wait_for_move_end()
149        curr_motor = self.chamber_inst.query("POS:CURR:MOT?")
150        curr_azim, curr_roll = map(float, (curr_motor.split(',')))
151        self.current_azim = curr_azim
152        self.current_roll = curr_roll
153        return curr_azim, curr_roll
154
155    def sweep_setup(self, azim_sss: tuple, roll_sss: tuple, sweep_type: str):
156        self.chamber_inst.write(
157            f"POS:SWE:AZIM:STAR {azim_sss[0]};STOP {azim_sss[1]};STEP {azim_sss[2]}"
158        )
159        self.chamber_inst.write(
160            f"POS:SWE:ROLL:STAR {roll_sss[0]};STOP {roll_sss[1]};STEP {roll_sss[2]}"
161        )
162        self.chamber_inst.write(f"POS:SWE:TYPE {sweep_type}")
163        self.chamber_inst.write("POS:SWE:CONT 1")
164
165    def sweep_init(self):
166        def query_float_list(inst, scpi):
167            resp = inst.query(scpi)
168            return list(map(float, resp.split(',')))
169
170        self.chamber_inst.write("POS:SWE:INIT")
171        self.wait_for_sweep_end()
172        azims = query_float_list(self.chamber_inst, "FETC:AZIM?")
173        rolls = query_float_list(self.chamber_inst, "FETC:ROLL?")
174        phis = query_float_list(self.chamber_inst, "FETC:DUT:PHI?")
175        thetas = query_float_list(self.chamber_inst, "FETC:DUT:THET?")
176        return zip(azims, rolls, phis, thetas)