from . import _laser as las
from ._agilent_lightwave_connection import AgilentLightwaveConnection
import time
import struct
import numpy as np
from scipy import interpolate
import os
import warnings
[docs]class LaserAgilent8164B(AgilentLightwaveConnection, las.LaserTunable):
'''
Controls the laser module in the Agilent 8164B.
Args:
gpib_num (int): The GPIB bus number the laser is on.
gpib_dev_num (int): The device number the laser in on
the bus.
power_unit (str): Either \'W\' or \'dBm\' depending on what
units the Agilent 8164B should use for the laser power.
output_mode (str):
'HIGH' -> The High Power output is regulated.
'LOWS' -> The Low SSE output is regulated.
'BHR' -> Both outputs are active but only the High Power output is Regulated.
'BLR' -> Both outputs are active but only the Low SSE output is Regulated.
'''
def __init__(self, gpib_num=None, gpib_dev_num=None, power_unit='W',
output_mode='high'):
super().__init__(gpib_num, gpib_dev_num)
self._power_unit = self.set_unit(power_unit)
self._set_output_mode(output_mode)
[docs] def turn_on(self):
# Turn laser on.
self._write('outp1')
# Open output shutter.
self._write('sour0:pow:stat 1')
return self.get_on_or_off()
[docs] def turn_off(self):
# Turn laser off.
self._write('outp0')
# Open output shutter.
self._write('sour0:pow:stat 0')
return self.get_on_or_off()
[docs] def get_on_or_off(self):
on_off = self._query('sour0:pow:stat?')
return bool(int(on_off))
[docs] def set_power_W(self, power_W):
assert power_W < 5.e-3, 'Can\'t set power that high.'
if self._power_unit == 'dBm':
power_dbm = LaserAgilent8164B.watts_to_dbm(power_W)
cmd = 'sour0:pow %fdbm' % power_dbm
elif self._power_unit == 'W':
cmd = 'sour0:pow %fw' % power_W
self._write(cmd)
return self.get_power_W()
[docs] def get_power_W(self):
inString = 'sour0:pow?'
data = self._query(inString)
return float(data)
[docs] def set_wavelength_m(self, wavelength_m):
cmd = 'sour0:wav %sM' % wavelength_m
self._write(cmd)
r = self.get_wavelength_m()
return r
[docs] def get_wavelength_m(self):
cmd = 'sour0:wav?'
r = float(self._query(cmd))
return r
[docs] def set_unit(self, unit):
'''
Sets the units the laser should operate in.
This will affect in what units the laser power
is displayed on the screen.
Args:
unit (str): Either \'dBm\' or \'W\'.
Returns:
str: The units the laser is oeprating in.
'''
assert unit.lower() in ('dbm', 'w')
cmd = 'sour0:pow:unit %s' % unit
self._write(cmd)
self._power_unit = self.get_unit()
return self._power_unit
[docs] def get_unit(self):
'''
Gets the units the laser is operating in.
The units affect in what units the laser power
is displayed on the screen.
Returns:
str: The units the laser is oeprating in.
'''
inString = 'sour0:pow:unit?'
data = self._query(inString)
unit = int(data)
if unit == 0:
unit_str = 'dBm'
else:
unit_str = 'W'
return unit_str
[docs] def last_operation_completed(self):
return self._query('*OPC?')
[docs] def wait_for_last_operation_completed(self):
finish = False
while not finish:
finish = self.last_operation_completed()
return finish
def _get_output_mode(self):
m = self._query('outp0:path?')
return m
def _set_output_mode(self, mode):
'''
Sets the mode the power meter will operate in.
Args:
mode (str):
HIGH -> The High Power output is regulated.
LOWS -> The Low SSE output is regulated.
BHR -> Both outputs are active but only the High Power output is Regulated.
BLR -> Both outputs are active but only the Low SSE output is Regulated.
Returns:
str: The mode the laser is operating in.
'''
m = mode.lower()
assert m in ('high', 'lows', 'bhr', 'blr')
self._write('outp0:path %s' % m)
return self._get_output_mode()
[docs] def get_velocity_nm_s(self):
return float(self._query('sour0:wav:swe:spe?'))
[docs] def set_velocity_nm_s(self, sweep_speed_nm_per_sec):
assert float(sweep_speed_nm_per_sec) in (0.5, 1., 2., 5., 10., 20., 40., 80.), \
'Invalid sweep speed choice; laser only supports certain sweep speeds nm/s.'
self._write('sour0:wav:swe:spe %fnm/s' % sweep_speed_nm_per_sec)
return self.get_velocity_nm_s()
[docs] def wavelength_sweep(self,
start_wavelength_nm,
stop_wavelength_nm,
step_wavelength_nm,
max_power_mW,
sweep_speed_nm_per_sec=5.,
power_metre_slot=1,
int_time_ms=20.,
filename=None,
preserve_settings=True):
assert step_wavelength_nm >= 0.6e-3, '`step_wavelength_nm` to small.'
max_power_W = max_power_mW / 1000.
int_time_us = int_time_ms * 1000.
int_time_s = int_time_ms / 1000.
# Save current settings
if preserve_settings:
cmds = ['trig:conf?',
'sour0:pow:stat?',
'trig0:outp?',
'trig0:inp?',
'sour0:wav:swe:star?',
'sour0:wav:swe:stop?',
'sour0:wav:swe:step?',
'sour0:wav:swe:mode?',
'sour0:wav:swe:llog?',
'trig1:inp?',
'sens'+str(power_metre_slot)+':pow:unit?',
'sens'+str(power_metre_slot)+':pow:rang:auto?',
'sens'+str(power_metre_slot)+':pow:rang?',
'sens'+str(power_metre_slot)+':pow:wav?',
'sens'+str(power_metre_slot)+':func:par:logg?']
temps = [self._query(cmd) for cmd in cmds]
max_power_dbm = LaserAgilent8164B.watts_to_dbm(max_power_W)
# Setup lasers and triggers.
self._write('trig:conf loop')
self._write('sour0:pow:stat 1')
self._query('sour0:pow:stat?')
self._write('trig0:outp stf')
self._write('trig0:inp sws')
self._write('sour0:wav:swe:star %fnm' % start_wavelength_nm)
self._write('sour0:wav:swe:stop %fnm' % stop_wavelength_nm)
self._write('sour0:wav:swe:step %fnm' % step_wavelength_nm)
self.set_velocity_nm_s(sweep_speed_nm_per_sec)
self._write('sour0:wav:swe:mode cont')
self._write('sour0:wav:swe:llog 1')
param_check = self._query('sour0:wav:swe:chec?').strip()
if param_check != '0,OK':
raise RuntimeError('Invalid sweep parameters: `%s`.' % param_check)
max_selectable_power_W = float(self._query('sour0:wav:swe:pmax? %fnm,%fnm' %
(start_wavelength_nm, stop_wavelength_nm)))
if max_power_W > max_selectable_power_W:
raise RuntimeError('Invalid `max_power` choice.')
self._write('sour0:pow %fW' % max_power_W)
# This command doesn't work for our laser module: int(self._query('sour0:wav:swe:exp?'))
# so it must be manually calculated.
expected_triggers = int(self._query('sour0:wav:swe:exp?'))
power_meas_time = int_time_s*expected_triggers
sweep_time = (stop_wavelength_nm-start_wavelength_nm)/sweep_speed_nm_per_sec
if power_meas_time <= sweep_time:
warnings.warn('The time needed for power readings %.2fsec is longer than the sweep %.2fsec. Either reduce the integration time of the power meter, reduce the sweep speed, or reduce the sampling rate (`step_wavelength_nm`).' % (power_meas_time, sweep_time))
self._write('sour0:wav:swe 1')
# Setup power meter.
self._write('trig1:inp sme')
self._write('sens'+str(power_metre_slot)+':pow:unit 1')
self._write('sens'+str(power_metre_slot)+':pow:rang:auto 0')
self._write('sens'+str(power_metre_slot)+':pow:rang %fdbm' % max_power_dbm)
self._query('sens'+str(power_metre_slot)+':pow:rang?')
centre_wavelength_nm = 0.5*(start_wavelength_nm+stop_wavelength_nm)
self._write('sens'+str(power_metre_slot)+':pow:wav %fnm' % centre_wavelength_nm)
self._write('sens'+str(power_metre_slot)+':func:par:logg %i,%ius' % (expected_triggers, int_time_us))
# Set to one trigger per measurment.
self._write('sens'+str(power_metre_slot)+':func:stat logg,star')
finished = False
while not finished:
flag = int(self._query('source0:wav:sweep:flag?').strip())
finished = bool(flag)
self._write('sour0:wav:swe:soft')
# Wait for log to complete.
while self._query('sour0:wav:sweep:state?').strip() != '+0':
time.sleep(0.1)
# Version from manual. Doesn't work.
#while self._query('sens'+str(power_metre_slot)+':func:stat?').strip() != 'LOGGING_STABILITY,COMPLETE':
# self._query('sens'+str(power_metre_slot)+':func:stat?')
# time.sleep(50.e-3)
# Get logged data.
self._write('sens'+str(power_metre_slot)+':func:res?')
r = self._read(2)
assert r[0] == '#', 'A \'#\' should have been read first.'
size_of_num_bytes = int(r[1])
num_bytes = int(self._read(size_of_num_bytes))
data = self._read_raw(num_bytes)
_ = self._read(1) # Read the remaining '\n' character.
data = [data[i:i+4] for i in range(0, len(data), 4)]
powers = [struct.unpack('f', d)[0] for d in data]
while int(self._query('sour0:wav:swe:flag?')) not in (flag+1, flag+2):
time.sleep(50.e-3)
self._write('sour0:read:data? llog')
r = self._read(2)
assert r[0] == '#', 'A \'#\' should have been read first.'
size_of_num_bytes = int(r[1])
num_bytes = int(self._read(size_of_num_bytes))
data = self._read_raw(num_bytes)
_ = self._read(1) # Read the remaining '\n' character.
data = [data[i:i+8] for i in range(0, len(data), 8)]
wavelengths = [struct.unpack('d', d)[0] for d in data]
# Wait for sweep to finish incase it is still running.
while int(self._query('sour0:wav:swe?')) != 0:
time.sleep(50.e-3)
# Reload settings
if preserve_settings:
for cmd, temp in zip(cmds, temps):
self._write(cmd[:-1] + ' ' + temp)
# Save data to file if necessary.
try:
with open(filename, 'w') as fs:
for w, p in zip(wavelengths, powers):
fs.write('%e,%e\n' % (w, p))
except TypeError:
pass
assert expected_triggers == len(wavelengths) == len(powers), 'Invalid data.'
return wavelengths, powers
[docs] def wavelength_sweep_interp(self,
start_wavelength_nm,
stop_wavelength_nm,
step_wavelength_nm,
max_power_W,
sweep_speed_nm_per_sec=5.,
filename=None):
wavelengths, powers = self.wavelength_sweep(start_wavelength_nm,
stop_wavelength_nm,
step_wavelength_nm,
max_power_W,
sweep_speed_nm_per_sec,
filename)
powers_interp = interpolate.interp1d(wavelengths, powers)
return powers_interp