• 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
7from ota_chamber import Chamber
8
9
10class Chamber(Chamber):
11    """Base class implementation for signal generators.
12
13    Base class provides functions whose implementation is shared by all
14    chambers.
15    """
16    CHAMBER_SLEEP = 10
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()
23        self.chamber_inst = self.chamber_resource.open_resource(
24            '{}::{}::{}::INSTR'.format(self.config['network_id'],
25                                       self.config['ip_address'],
26                                       self.config['hislip_interface']))
27        self.chamber_inst.timeout = 200000
28        self.chamber_inst.write_termination = '\n'
29        self.chamber_inst.read_termination = '\n'
30
31        self.id_check(self.config)
32        self.current_azim = 0
33        self.current_roll = 0
34        if self.config.get('reset_home', True):
35            self.find_chamber_home()
36            self.move_theta_phi_abs(self.config['chamber_home']['theta'],
37                                    self.config['chamber_home']['phi'])
38            self.set_new_home_position()
39        else:
40            self.config['chamber_home'] = {'phi': 0, 'theta': 0}
41            self.log.warning(
42                'Reset home set to false. Assumed [0,0]. Chamber angles may not be as expected.'
43            )
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 chamber to [{}, {}]".format(theta, phi))
91        self.move_to_azim_roll(phi, theta)
92
93    def move_phi_rel(self, phi):
94        self.log.info("Moving Phi by {} degrees".format(phi))
95        self.move_to_azim_roll(self.current_azim + phi, self.current_roll)
96
97    def move_theta_rel(self, theta):
98        self.log.info("Moving Theta by {} degrees".format(theta))
99        self.move_to_azim_roll(self.current_azim, self.current_roll + theta)
100
101    def move_feed_roll(self, roll):
102        self.log.info("Moving feed roll to {} degrees".format(roll))
103        self.chamber_inst.write(f"POS:MOVE:ROLL:FEED {roll}")
104        self.chamber_inst.write("POS:MOVE:INIT")
105        self.wait_for_move_end()
106        self.current_feed_roll = self.chamber_inst.query("POS:MOVE:ROLL:FEED?")
107
108    def reset_phi(self):
109        self.log.info("Resetting Phi.")
110        self.move_to_azim_roll(0, self.current_roll)
111        self.phi = 0
112
113    def reset_theta(self):
114        self.log.info("Resetting Theta.")
115        self.move_to_azim_roll(self.current_azim, 0)
116        self.theta = 0
117
118    def reset_phi_theta(self):
119        """ Resets signal generator."""
120        self.log.info("Resetting to home.")
121        self.chamber_inst.write(f"POS:ZERO:GOTO")
122        self.wait_for_move_end()
123
124    # Keysight-provided functions
125    def wait_for_move_end(self):
126        moving_bitmask = 4
127        while True:
128            stat = int(self.chamber_inst.query("STAT:OPER:COND?"))
129            if (stat & moving_bitmask) == 0:
130                return
131            time.sleep(0.25)
132
133    def wait_for_sweep_end(self):
134        sweeping_bitmask = 16
135        while True:
136            stat = int(self.chamber_inst.query("STAT:OPER:COND?"))
137            if (stat & sweeping_bitmask) == 0:
138                return
139            time.sleep(0.25)
140
141    def move_to_azim_roll(self, azim, roll):
142        self.chamber_inst.write(f"POS:MOVE:AZIM {azim};ROLL {roll}")
143        self.chamber_inst.write("POS:MOVE:INIT")
144        self.wait_for_move_end()
145        curr_motor = self.chamber_inst.query("POS:CURR:MOT?")
146        curr_azim, curr_roll = map(float, (curr_motor.split(',')))
147        self.current_azim = curr_azim
148        self.current_roll = curr_roll
149        return curr_azim, curr_roll
150
151    def sweep_setup(self, azim_sss: tuple, roll_sss: tuple, sweep_type: str):
152        self.chamber_inst.write(
153            f"POS:SWE:AZIM:STAR {azim_sss[0]};STOP {azim_sss[1]};STEP {azim_sss[2]}"
154        )
155        self.chamber_inst.write(
156            f"POS:SWE:ROLL:STAR {roll_sss[0]};STOP {roll_sss[1]};STEP {roll_sss[2]}"
157        )
158        self.chamber_inst.write(f"POS:SWE:TYPE {sweep_type}")
159        self.chamber_inst.write("POS:SWE:CONT 1")
160
161    def sweep_init(self):
162
163        def query_float_list(inst, scpi):
164            resp = inst.query(scpi)
165            return list(map(float, resp.split(',')))
166
167        self.chamber_inst.write("POS:SWE:INIT")
168        self.wait_for_sweep_end()
169        azims = query_float_list(self.chamber_inst, "FETC:AZIM?")
170        rolls = query_float_list(self.chamber_inst, "FETC:ROLL?")
171        phis = query_float_list(self.chamber_inst, "FETC:DUT:PHI?")
172        thetas = query_float_list(self.chamber_inst, "FETC:DUT:THET?")
173        return zip(azims, rolls, phis, thetas)
174
175    def configure_positioner(self, pos_name, pos_visa_addr):
176        select = "True"
177        simulate = "False"
178        options = ""
179        data = f"'{pos_name}~{select}~{simulate}~{pos_visa_addr}~{options}'"
180        self.chamber_inst.write(f"EQU:CONF {data}")
181        self.chamber_inst.write("EQU:UPD")
182