/lib/instruments.py

https://github.com/pruan/TestDepot · Python · 2725 lines · 2074 code · 375 blank · 276 comment · 304 complexity · e41fb248beb7871b14689746d524b862 MD5 · raw file

Large files are truncated click here to view the full file

  1. #!/usr/bin/python
  2. # -*- coding: ascii -*-
  3. # vim:ts=4:sw=4:softtabstop=4:smarttab:expandtab
  4. #
  5. # Copyright (C) 1999-2005 Stratalight Communications.
  6. #
  7. """
  8. Module where instrument objects are defined. These are persistent objects that
  9. can be stored in the Stratatest storage.
  10. """
  11. __all__ = ['InstrumentError', 'VISASyntaxError', 'GPIBInvalidCommand',
  12. 'VISAResource', 'GPIBResource', 'Bitfield', 'EventStatusEnableRegister',
  13. 'ServiceRequestEnableRegister', 'Block', 'Boolean', 'Identity', 'SCPIFloat',
  14. 'GpibInstrumentWrapper', 'Instrument', 'InstrumentChassis', 'InstrumentModule',
  15. 'InstrumentPort', 'DMM', 'RFSignalGenerator', 'Oscilloscope', 'HP53131A',
  16. 'FunctionGenerator', 'DCPower', 'SpectrumAnalyzer', 'OpticalSpectrumAnalyzer',
  17. 'PowerMeter', 'OpticalSignalGenerator', 'OpticalPowerAmp', 'OpticalPowerMeter',
  18. 'OpticalAttenuator', 'FIBERAMP', 'HP8163A', 'HP8163B', 'HP81635A', 'HP81635APort',
  19. 'HP81591A', 'HP81594B', 'HP8159xPort', 'MAP', 'OpticalAttenuator_JDSU',
  20. 'HP81570A', 'HP81570APort',
  21. 'HP81576A', 'HP81576APort',
  22. 'HP81663A', 'HP81689A', 'HP816xxAPort', 'HP81689APort', 'HP81663APort',
  23. 'HP86120C', 'HP8156A', 'HP34401A', 'HP86100A', 'HP34970A', 'HP33120A',
  24. 'HP6674A', 'HP81619A', 'HP81619APort', 'HP81633A', 'HP81633APort',
  25. 'HP83650L', 'AQ6317B', 'AQ6370',
  26. 'GP700', 'IA3',
  27. 'Graph', 'Marker', 'Modulation', 'plug_module',
  28. 'unplug_module', 'get_quantity', 'update_instruments', 'get_class',
  29. 'get_devices', 'clear_controller', 'KEOPSYSAMP']
  30. from copy import copy
  31. import sys
  32. from time import clock, sleep
  33. from netobjects import PersistentList, PersistentDict, PersistentData, MANDATORY, CONTAINER
  34. import scheduler
  35. import stratabuiltins # only needs to be imported, nothing more.
  36. import slstorage
  37. import pyvisa.visa as visa
  38. import pyvisa.vpp43 as vpp43
  39. import pyvisa.visa_exceptions as visa_exceptions
  40. import time
  41. import math
  42. from dataAnalysisLib import avg
  43. # instrument exceptions
  44. class InstrumentError(ValueError):
  45. pass
  46. class VISASyntaxError(InstrumentError):
  47. pass
  48. class GPIBInvalidCommand(InstrumentError):
  49. pass
  50. add_exception(InstrumentError)
  51. add_exception(VISASyntaxError)
  52. add_exception(GPIBInvalidCommand)
  53. # VISA objects
  54. class VISAResource(object):
  55. pass
  56. class GPIBResource(VISAResource):
  57. """GPIB[board]::primary address[::secondary address][::INSTR]"""
  58. def __init__(self, board=0, address=0, secondary=None):
  59. self.board = int(board)
  60. self.address = int(address)
  61. self.secondary = secondary
  62. def __repr__(self):
  63. return "%s(%r, %r, %r)" % (self.__class__.__name__, self.board, self.address, self.secondary)
  64. def __str__(self):
  65. if secondary is None:
  66. return "GPIB%s::%s::INSTR" % (self.board, self.address)
  67. else:
  68. return "GPIB%s::%s::%s::INSTR" % (self.board, self.address, self.secondary)
  69. def parse(self, text):
  70. parts = text.split("::")
  71. if len(parts) == 4:
  72. self.board, self.address, self.secondary = int(parts[0][4:]), int(parts[1]), int(parts[2])
  73. elif len(parts) in (2, 3):
  74. self.board, self.address, self.secondary = int(parts[0][4:]), int(parts[1]), None
  75. else:
  76. raise VISASyntaxError, "invalid GPIB resource: %s" % (text,)
  77. class Bitfield(object):
  78. "Mutable set of bits that also looks like an integer."
  79. _NAMES = {
  80. }
  81. def __init__(self, init_bits=0, numbits=32):
  82. self.bits = int(init_bits)
  83. if numbits < 1 or numbits > 32:
  84. raise ValueError, "bits must be 1:32"
  85. self._size = numbits
  86. def getbit(self, bit):
  87. return self.bits & (1 << bit)
  88. def setbit(self, bit):
  89. self.bits |= (1 << bit)
  90. def clearbit(self, bit):
  91. self.bits &= ~(1 << bit)
  92. def __str__(self):
  93. s = ["Bits set:"]
  94. names = self._NAMES
  95. for bit in range(self._size):
  96. if self.bits & 2**bit:
  97. name = names.get(bit)
  98. if name:
  99. s.append(name)
  100. else:
  101. s.append("Bit %d" % (bit,))
  102. return "\n".join(s)
  103. def __int__(self):
  104. return self.bits & ~(0xffffffff << self._size)
  105. def __getitem__(self, bit):
  106. return self.bits & (1 << bit)
  107. def __setitem__(self, bit, val):
  108. if bool(val):
  109. self.bits |= (1 << bit)
  110. else:
  111. self.bits &= ~(1 << bit)
  112. class EventStatusEnableRegister(Bitfield):
  113. _NAMES = {
  114. 7: "Power On",
  115. 6: "User Request",
  116. 5: "Command Error",
  117. 4: "Execution Error",
  118. 3: "Device dependent Error",
  119. 2: "Query Error",
  120. 1: "Request Control",
  121. 0: "Operation Complete",
  122. }
  123. def __init__(self, init_bits=0):
  124. super(EventStatusEnableRegister, self).__init__(init_bits, 8)
  125. class ServiceRequestEnableRegister(Bitfield):
  126. _NAMES = {
  127. 7: "Operation Status",
  128. 6: "Request Status",
  129. 5: "Event Status Byte",
  130. 4: "Message Available",
  131. 3: "Questionable Status",
  132. }
  133. def __init__(self, init_bits=0):
  134. super(ServiceRequestEnableRegister, self).__init__(init_bits, 8)
  135. class Block(object):
  136. def __init__(self, data):
  137. self.parse(data)
  138. def __str__(self):
  139. l = len(self.data)
  140. H = len(str(l))
  141. return "#%d%d%s" % (H, l, self.data)
  142. def parse(self, string):
  143. if string.startswith("#"):
  144. H = int(string[1])
  145. l = int(string[2:2+H])
  146. self.data = string[l+2:l]
  147. else:
  148. self.data = string
  149. class Boolean(object):
  150. def __init__(self, data):
  151. try:
  152. self.value = bool(int(data))
  153. except ValueError:
  154. if type(data) is str:
  155. data = data.lower()
  156. if data in ("on", "yes", "true", "enable", "ok"):
  157. self.value = True
  158. elif data in ("off", "no", "false", "disable", "notok"):
  159. self.value = False
  160. else:
  161. raise ValueError, "invalid value for Boolean: %s" % (data,)
  162. def __str__(self):
  163. return IF(self.value, "ON", "OFF")
  164. def __repr__(self):
  165. return "Boolean(%r)" % (self.value,)
  166. def __nonzero__(self):
  167. return self.value
  168. def __int__(self):
  169. return int(self.value)
  170. class Identity(object):
  171. """An equipment Identity. Returned from identify() instrument method."""
  172. def __init__(self, idtext=None):
  173. if idtext:
  174. idtext = idtext.strip()
  175. if idtext:
  176. self.parse(idtext)
  177. else:
  178. self.clear()
  179. else:
  180. self.clear()
  181. def clear(self):
  182. self.manufacturer = None
  183. self.model = None
  184. self.serial = None
  185. self.firmware = None
  186. def __str__(self):
  187. return """IDN: MFG: %s Model: %s Serial: %s Firmware: %s""" % (self.manufacturer, self.model, self.serial, self.firmware)
  188. # some equipment have five fields, most have four.
  189. def parse(self, text):
  190. commas = text.count(",")
  191. if commas > 3:
  192. self.manufacturer, model1, model2, self.serial, self.firmware = self._chop(text, 4)
  193. self.model = model1+model2
  194. elif commas > 2:
  195. self.manufacturer, self.model, self.serial, self.firmware = self._chop(text, 3)
  196. else:
  197. raise ValueError, "identity string appears invalid: %r" % (text,)
  198. def _chop(self, text, commas):
  199. return map(lambda s: s.replace(" ", ""), text.split(",",commas))
  200. # JDSU module ID is NOT standard IEEE
  201. class Identity_JDSU(object):
  202. """An equipment Identity. Returned from identify() instrument method."""
  203. def __init__(self, idtext=None):
  204. if idtext:
  205. idtext = idtext.strip()
  206. if idtext:
  207. #print "JDSU IDTEXT: %s" % idtext
  208. self.parse(idtext)
  209. else:
  210. self.clear()
  211. else:
  212. self.clear()
  213. def clear(self):
  214. self.manufacturer = None
  215. self.model = None
  216. self.serial = None
  217. self.firmware = None
  218. def __str__(self):
  219. return """IDN: MFG: %s Model: %s Serial: %s Firmware: %s""" % (self.manufacturer, self.model, self.serial, self.firmware)
  220. # some equipment have five fields, most have four.
  221. def parse(self, text):
  222. commas = text.count(",")
  223. if commas > 3:
  224. self.serial, self.model, model2, junk, self.firmware = self._chop(text, 4)
  225. self.manufacturer = 'JDSU'
  226. print " === model: %s" % self.model
  227. elif commas > 2:
  228. self.serial, self.model, self.firmware = self._chop(text, 3)
  229. self.manufacturer = 'JDSU'
  230. print "**** model: %s" % self.model
  231. else:
  232. raise ValueError, "identity string appears invalid: %r" % (text,)
  233. def _chop(self, text, commas):
  234. return map(lambda s: s.replace(" ", ""), text.split(",",commas))
  235. # a float with special values defined by SCPI
  236. class SCPIFloat(float):
  237. def __str__(self):
  238. if self == 9.9e37:
  239. return "INF"
  240. elif self == -9.9e37:
  241. return "NINF"
  242. elif self == 9.91e37:
  243. return "NaN"
  244. else:
  245. return float.__str__(self)
  246. ### constants and enumerations ###
  247. ### Some Instrument object attributes may be set to one of these values.
  248. DEFAULT = "DEF"
  249. MIN = "MIN"
  250. MAX = "MAX"
  251. ON = Enum(1, "ON")
  252. OFF = Enum(0, "OFF")
  253. SWEEP_STOP = Enum(0, "SWEEP_STOP")
  254. SWEEP_SINGLE = Enum(1, "SWEEP_SINGLE")
  255. SWEEP_CONTINUOUS = Enum(2, "SWEEP_CONTINUOUS") # same as REPEAT
  256. SWEEP_AUTO = Enum(3, "SWEEP_AUTO")
  257. SWEEP_SEGMENTMEASURE = Enum(4, "SWEEP_SEGMENTMEASURE")
  258. INF = SCPIFloat(9.9e37)
  259. NINF = SCPIFloat(-9.9e37)
  260. NaN = SCPIFloat(9.91e37)
  261. ## support for general modulation parameters ##
  262. # modulation types
  263. DC = NOMOD = Enum(0, "DC")
  264. AM = Enum(1, "AM")
  265. FM = Enum(2, "FM")
  266. PSK = Enum(3, "PSK")
  267. FSK = Enum(4, "FSK")
  268. # modulation subtypes
  269. SBSC = Enum(0, "SBSC") # FM subtype
  270. # modulation sources
  271. INT = Enum(0, "INT") # internal digital modulation
  272. INT1 = Enum(0, "INT1")
  273. INT2 = COHC = Enum(1, "INT2") # coherence control
  274. EXT = AEXT = Enum(2, "EXT") # external analog modulation
  275. DEXT = Enum(3, "DEXT") # external digital modulation
  276. WVLL = Enum(5, "WVLL") # wavelength locking
  277. BACK = Enum(6, "BACK") # backplane - external digital modulation using Input Trigger Connector
  278. # laser source selection
  279. LASER_EXTERNAL = Enum(0, "EXT")
  280. LASER_LOWER = Enum(1, "LOW")
  281. LASER_UPPER = Enum(2, "UPP")
  282. LASER_BOTH = Enum(3, "BOTH")
  283. # oscilloscope and spectrum analyzer control values
  284. PEAK = Enum(100, "PEAK")
  285. CENTER = Enum(101, "CENTER")
  286. REFERENCE = Enum(102, "REFERENCE")
  287. SCALE_WAVELENGTH = Enum(0, "SCALE_WAVELENGTH")
  288. SCALE_FREQUENCY = Enum(1, "SCALE_FREQUENCY")
  289. class GpibInstrumentWrapper(object):
  290. """Wrapper for VISA sessions that allows logging."""
  291. def __init__(self, inst, logfile):
  292. self._inst = inst
  293. self._logfile = logfile
  294. self._use_srq = False
  295. def __getattr__(self, key):
  296. return getattr(self._inst, key)
  297. def set_logfile(self, logfile):
  298. self._logfile = logfile
  299. def read(self):
  300. if self._use_srq:
  301. self.wait_for_srq()
  302. rv = self._inst.read()
  303. if self._logfile:
  304. self._logfile.write("VISA read: %s: %r\n" % (self._inst.resource_name, rv))
  305. return rv
  306. def write(self, text, wait=False):
  307. if self._logfile:
  308. self._logfile.write("VISA write: %s: %r\n" % (self._inst.resource_name, text))
  309. self._inst.write(text)
  310. if wait:
  311. cmd = "*OPC?"
  312. retries = 10
  313. while retries > 0:
  314. # do the command again
  315. self._inst.write(text)
  316. comp = int(self.ask(cmd))
  317. #print "comp: %s, retries: %s" % (comp, retries)
  318. if comp:
  319. break
  320. else:
  321. #print "resource: %s" % self._inst.resource_name
  322. self._inst.write(text)
  323. comp = int(self.ask(cmd))
  324. time.sleep(1)
  325. retries -= 1
  326. if not comp:
  327. raise InstrumentError, "Operation %r did not complete." % (text,)
  328. def ask(self, cmd):
  329. self._inst.write(cmd)
  330. if self._use_srq:
  331. self.wait_for_srq()
  332. rv = self._inst.read()
  333. if self._logfile:
  334. self._logfile.write("VISA ask: %s: %r -> %r\n" % (self._inst.resource_name, cmd, rv))
  335. return rv
  336. def _get_stb(self):
  337. return vpp43.read_stb(self._inst.vi)
  338. STB = property(_get_stb, None, None, "Status byte (STB)")
  339. def use_srq(self, flag=True):
  340. self._use_srq = bool(flag)
  341. srquse = property(lambda s: s._use_srq, use_srq, None, "SRQ line in use? Set to True to use SRQ line.")
  342. def wait_for_srq(self, timeout=250):
  343. vpp43.enable_event(self._inst.vi, vpp43.VI_EVENT_SERVICE_REQ, vpp43.VI_QUEUE)
  344. if timeout and not(0 <= timeout <= 4294967):
  345. raise ValueError, "timeout value is invalid"
  346. starting_time = clock()
  347. try:
  348. while True:
  349. if timeout is None:
  350. adjusted_timeout = vpp43.VI_TMO_INFINITE
  351. else:
  352. adjusted_timeout = max(0, int((starting_time + timeout - clock()) * 1000))
  353. event_type, context = vpp43.wait_on_event(self._inst.vi, vpp43.VI_EVENT_SERVICE_REQ, adjusted_timeout)
  354. vpp43.close(context)
  355. if vpp43.read_stb(self._inst.vi) & 0x40:
  356. break
  357. finally:
  358. vpp43.discard_events(self._inst.vi, vpp43.VI_EVENT_SERVICE_REQ, vpp43.VI_QUEUE)
  359. def get_SRE(self):
  360. res = self.ask("*SRE?")
  361. return ServiceRequestEnableRegister(int(res))
  362. def set_SRE(self, val):
  363. val = int(val) & 0xFF
  364. self.write("*SRE %d" % (val,))
  365. SRE = property(get_SRE, set_SRE, None, "SRE value")
  366. def get_ESE(self):
  367. res = self.ask("*ESE?")
  368. return EventStatusEnableRegister(int(res))
  369. def set_ESE(self, val):
  370. val = int(val) & 0xFF
  371. self.write("*ESE %d" % (val,))
  372. ESE = property(get_ESE, set_ESE)
  373. # abstract base class for instruments. Don't use this directly. This is a
  374. # persistent object that is also stored in the configuration.
  375. class Instrument(PersistentData):
  376. "A generic virtual instrument. It holds the base attributes."
  377. _ATTRS = {
  378. "name": (str, "unknown"),
  379. "manufacturer": (str, "unknown"),
  380. "modelNumber": (str, "unknown"),
  381. "serialNumber": (str, "unknown"),
  382. "firmware": (str, "unknown"),
  383. "password": (str, "1234"),
  384. "visa_resource": (str, ""), # e.g.'GPIB2::22'
  385. }
  386. def __str__(self):
  387. return "%s: %s %s (SN: %s)" % (self.name, self.manufacturer, self.modelNumber, self.serialNumber)
  388. def get_agent(self, logfile=None):
  389. """Returns a VISA Instrument instance to control the instrument."""
  390. try:
  391. return self._cache["_agent"]
  392. except KeyError:
  393. if self.visa_resource:
  394. a = scheduler.get_scheduler().timeout(visa.GpibInstrument, args=(self.visa_resource,))
  395. else:
  396. raise ValueError, "Instrument: visa_resource not set."
  397. a = GpibInstrumentWrapper(a, logfile or self._cache.get("_logfile"))
  398. self._cache["_agent"] = a
  399. self.initialize(a)
  400. return a
  401. def clear_agent(self):
  402. try:
  403. a = self._cache.pop("_agent")
  404. except KeyError:
  405. pass
  406. else:
  407. a.clear()
  408. a.close()
  409. # override if agent needs initialization after creation
  410. def initialize(self, agent):
  411. pass
  412. def set_logfile(self, lf):
  413. self._cache["_logfile"] = lf
  414. def clear_controller(self):
  415. self.clear_agent()
  416. clear_controller(self.visa_resource)
  417. def update(self):
  418. eqid = self.identify()
  419. self.manufacturer = eqid.manufacturer
  420. self.modelNumber = eqid.model
  421. self.serialNumber = eqid.serial
  422. self.firmware = eqid.firmware
  423. self.clear_agent()
  424. #print eqid
  425. def identify(self):
  426. return Identity(self.get_agent().ask("*IDN?"))
  427. def options(self):
  428. if self.name.startswith('MAP'):
  429. #print "Gettting GPIB address..."
  430. #cmd = "SYST:GPIB?"
  431. #print "CMD: %s" % cmd
  432. #print "GPIB: %s" % self.get_agent().ask(cmd)
  433. #cmd = "*IDN?"
  434. cmd = "SYST:LAY? 1"
  435. #cmd = ":SLOT1:IDN?"
  436. #cmd = "SYSTem:LAYout? 1"
  437. #cmd = "SYST:CARD:INFO? 1,1"
  438. #print "CMD: %s" % cmd
  439. raw = self.get_agent().ask(cmd)
  440. #print "raw: %s" % raw
  441. else:
  442. raw = self.get_agent().ask("*OPT?")
  443. print "RAW: %s" % raw
  444. return map(str.strip, raw.split(","))
  445. def test_result(self):
  446. res = self.get_agent().ask("*TST?")
  447. return int(res) # XXX
  448. def reset(self):
  449. self.get_agent().write("*RST")
  450. def clear_status(self):
  451. self.get_agent().write("*CLS")
  452. def wait(self):
  453. self.get_agent().write("*WAI")
  454. class InstrumentChassis(Instrument):
  455. """Instrument that is a chassis for holding other instrument modules."""
  456. _ATTRS = Instrument._ATTRS.copy()
  457. _ATTRS.update({
  458. "slots": (PersistentDict, CONTAINER), # slot maps to list index
  459. })
  460. SLOTBASE = 0
  461. # make the storage look like reality.
  462. def update(self):
  463. super(InstrumentChassis, self).update()
  464. for i, opt in enumerate(self.options()):
  465. if not opt:
  466. continue
  467. if (opt.endswith('BBS')) or (opt.endswith('EMP')) or (opt.endswith('UVL')):
  468. continue
  469. print "i: %s, opt: %s" % (i, opt)
  470. model = opt.split()[-1] # get rid of extraneous prefixes
  471. cls = get_class(model) # XXX true for all OPT? need spec...
  472. if cls is not None:
  473. #print "**** cls: %s" % cls
  474. inst = cls(visa_resource=self.visa_resource)
  475. try:
  476. plug_module(self, inst, i+self.SLOTBASE)
  477. except InstrumentError:
  478. unplug_module(self, i+self.SLOTBASE)
  479. plug_module(self, inst, i+self.SLOTBASE)
  480. inst.update()
  481. inst.name = "%s_%s" % (opt.strip(), inst.serialNumber)
  482. #print "xxx inst name: %s" % inst.name
  483. else:
  484. self.clear_agent()
  485. raise InstrumentError, "no instrument object defined for model: %s" % (model,)
  486. self.clear_agent()
  487. def plug_module(self, module, slot):
  488. return plug_module(self, module, slot)
  489. def unplug_module(self, module, slot):
  490. return unplug_module(self, module, slot)
  491. def is_empty(self, slot):
  492. "Indicate is given slot is empty or not."
  493. return bool(int(self.get_agent().ask(":SLOT%d:EMPT?" % (slot,))))
  494. def has_module(self, slot):
  495. "Indicate if a slot has something."
  496. return not self.is_empty()
  497. def get_slots(self):
  498. return self.slots.items()
  499. def get_modules(self, model=None):
  500. """Return a list of module with the given model name, or all modules if no model given."""
  501. if model is None:
  502. return self.slots.values()
  503. else:
  504. rv = []
  505. model = str(model)
  506. for slot, module in self.slots.items():
  507. if module.__class__.__name__.find(model) >= 0:
  508. rv.append(module)
  509. return rv
  510. def get_module(self, name):
  511. """Search for an installed module by model name. Returns module object,
  512. or None if not found."""
  513. if type(name) is int:
  514. return self.slots[name+self.SLOTBASE]
  515. for slot, module in self.slots.items():
  516. if module.__class__.__name__.find(name) >= 0:
  517. break
  518. else:
  519. return None # not found
  520. return module
  521. def __getitem__(self, name):
  522. mod = self.get_module(name)
  523. if mod:
  524. return mod
  525. else:
  526. raise KeyError, "module model name or index not found."
  527. class InstrumentModule(Instrument):
  528. """Instrument that is a module that fits into an InstrumentChassis."""
  529. _ATTRS = Instrument._ATTRS.copy()
  530. _ATTRS.update({
  531. "chassis": (InstrumentChassis, None), # parent object actually holding this module instance
  532. "slot": (int, 0), # slot
  533. })
  534. PORTBASE = 0
  535. def identify(self):
  536. if type(self) is IA3:
  537. #print "### Calling Identity_JDSU()..."
  538. self.name = 'IA3'
  539. return Identity_JDSU(self.get_agent().ask("SYST:CARD:INFO? 1,%s" %
  540. (self.slot,)), )
  541. else:
  542. #print "### Caling normal Identity()..."
  543. return Identity(self.get_agent().ask(":SLOT%d:IDN?" % (self.slot,)))
  544. def is_empty_head(self, head=0): # ack! I hope not!
  545. "Query if the port is is present."
  546. return int(self.get_agent().ask(":SLOT%d:HEAD%d:EMPT?" % (self.slot, head+self.PORTBASE)))
  547. def get_slot(self, slot=0):
  548. return self.slot
  549. def get_port(self, port=0):
  550. """Return a generic port. You probably want to override this in a subclass"""
  551. return self._get_port(InstrumentPort, port)
  552. def _get_master_port(self, portclass):
  553. if portclass.MASTER >= 0:
  554. return self._get_port(portclass, portclass.MASTER)
  555. else:
  556. return None
  557. def _get_port(self, portclass, port):
  558. #print "portclass: %s, port: %s" % (portclass, port)
  559. #print "portbase: %s" % self.PORTBASE
  560. if type (port) is int:
  561. return portclass(self, port+self.PORTBASE)
  562. elif type (port) is str and port.startswith("port"):
  563. port = int(port[4:])
  564. return portclass(self, port)
  565. else:
  566. raise ValueError, "invalid port number or name"
  567. def __getitem__(self, port):
  568. try:
  569. return self.get_port(port)
  570. except ValueError, err:
  571. raise KeyError, err
  572. def options(self):
  573. """Return connector options and instrument options"""
  574. return map(str.strip, self.get_agent().ask(":SLOT%d:OPT?" % (self.slot,)).split(","))
  575. # ports don't need to be stored and are non-persistent. they need an active session.
  576. class InstrumentPort(object):
  577. """Instrument that is a module that fits into an InstrumentChassis."""
  578. MASTER = -1 # some instruments have "master" ports that control settings for all ports.
  579. # a -1 value here means "no master". zero or greater indicates
  580. # the port number of the master.
  581. def __init__(self, module, port):
  582. self._agent = module.get_agent()
  583. self.slot = module.slot
  584. self.port = int(port)
  585. self.name = "%s.port%s" % (module.name, port)
  586. self.initialize()
  587. def __str__(self):
  588. return self.name
  589. def __del__(self):
  590. self.finalize()
  591. def is_master(self):
  592. if self.MASTER >= 0:
  593. return self.port == self.MASTER
  594. else:
  595. return True
  596. def initialize(self):
  597. pass
  598. def finalize(self):
  599. pass
  600. def identify(self):
  601. raw = self._agent.ask(":SLOT%d:HEAD%d:IDN?" % (self.slot,self.port)).strip()
  602. if raw:
  603. return Identity(raw)
  604. raw = self._agent.ask(":SLOT%d:IDN?" % (self.slot,)).strip()
  605. if raw:
  606. return Identity(raw)
  607. else:
  608. return Identity()
  609. def options(self):
  610. return self._agent.ask(":SLOT%d:HEAD%d:OPT?" % (self.slot,self.port))
  611. # virtually plug a module into a chassis...
  612. def plug_module(chassis, module, slot):
  613. #print "Calling plug...chassis: %s, module:%s, slot:%s" % (chassis, module, slot)
  614. if chassis.slots.has_key(slot):
  615. raise InstrumentError, "There's something in slot %s already." % (slot,)
  616. chassis.slots[slot] = module
  617. module.chassis = chassis
  618. module.slot = slot
  619. # unplug whatever is in slot from chassis object.
  620. def unplug_module(chassis, slot):
  621. module = chassis.slots.get(slot)
  622. if module:
  623. module.slot = -1
  624. del module.chassis
  625. del chassis.slots[slot]
  626. # classes for generic VISA equipment objects follow.
  627. class DMM(Instrument): # works with 34401A at least
  628. "A generic DMM object."
  629. def read_voltage(self, ac=False, range=20, resolution=0.001):
  630. a = self.get_agent()
  631. a.write(':MEASure:VOLTAGE:%s? %s,%s' % (IF(ac, "AC", "DC"), range, resolution))
  632. val = float(a.read())
  633. return PhysicalQuantity(round(val, 3), "V") # XXX fix round value
  634. def read_current(self, ac=False, range=1, resolution=0.001):
  635. a = self.get_agent()
  636. a.write(':MEASure:CURRENT:%s? %s,%s' % (IF(ac, "AC", "DC"), range, resolution))
  637. val = float(a.read())
  638. return PhysicalQuantity(round(val, 3), "A")
  639. class RFSignalGenerator(Instrument):
  640. "A generic RF signal generator."
  641. class Oscilloscope(Instrument):
  642. "A generic oscilloscope."
  643. class FunctionGenerator(Instrument):
  644. "A generic function generator."
  645. def _apply(self, function, frequency=DEFAULT, amplitude=DEFAULT, offset=DEFAULT):
  646. a = self.get_agent()
  647. return a.write("APPL:%s %s, %s, %s" % (function, frequency, amplitude, offset))
  648. def sinusoid(self, frequency=DEFAULT, amplitude=DEFAULT, offset=DEFAULT):
  649. return self._apply("SIN", frequency, amplitude, offset)
  650. def square(self, frequency=DEFAULT, amplitude=DEFAULT, offset=DEFAULT):
  651. return self._apply("SQU", frequency, amplitude, offset)
  652. def triangle(self, frequency=DEFAULT, amplitude=DEFAULT, offset=DEFAULT):
  653. return self._apply("TRI", frequency, amplitude, offset)
  654. def ramp(self, frequency=DEFAULT, amplitude=DEFAULT, offset=DEFAULT):
  655. return self._apply("RAMP", frequency, amplitude, offset)
  656. def noise(self, amplitude=DEFAULT, offset=DEFAULT):
  657. return self._apply("NOIS", DEFAULT, amplitude, offset)
  658. def DC(self, offset):
  659. return self._apply("DC", DEFAULT, DEFAULT, offset)
  660. def user(self, frequency=DEFAULT, amplitude=DEFAULT, offset=DEFAULT):
  661. return self._apply("USER", frequency, amplitude, offset)
  662. def output_load(self, imp):
  663. """output_load <impedence>
  664. Sets the output impedence to 50 ohms if impedenace is 50, open-circuit otherwise."""
  665. a = self.get_agent()
  666. if int(imp) != 50:
  667. imp = "INF"
  668. return a.write("OUT:LOAD %s" % (imp, ))
  669. def query(self):
  670. """Query the function generator and return:
  671. wave function, frequency, amplitude, dc offset, and output impedence."""
  672. a = self.get_agent()
  673. val = a.ask("APPLy?")[1:-1]
  674. imp = a.ask("OUT:LOAD?")
  675. fr, amp, offset = val.split(",")
  676. func, freq = fr.split()
  677. return func, float(freq), float(amp), float(offset), float(imp)
  678. class DCPower(Instrument):
  679. "A geneeric DC power supply."
  680. class SpectrumAnalyzer(Instrument):
  681. "A generic spectrum analyzer."
  682. class OpticalSpectrumAnalyzer(Instrument):
  683. "A generic optical spectrum analyzer."
  684. class PowerMeter(Instrument):
  685. "A generic power meter."
  686. class OpticalSignalGenerator(Instrument):
  687. "A generic optical signal generator."
  688. def initialize(self, agent):
  689. self.unlock()
  690. def is_locked(self):
  691. "Return state of laser lock."
  692. val = self.get_agent().ask(":LOCK?")
  693. return Boolean(val)
  694. def lock(self, tf=True, passwd=None):
  695. "Lock or unlock the laser functioning."
  696. tf = Boolean(tf)
  697. pw = passwd or self.password
  698. self.get_agent().write(":LOCK %s,%s" % (tf, pw[:4]))
  699. def unlock(self, passwd=None):
  700. self.lock(False, passwd)
  701. class OpticalPowerAmp(Instrument):
  702. "A generic optical power amplifier."
  703. class OpticalPowerMeter(Instrument):
  704. "A generic optical power meter (SCPI commands)."
  705. def initialize(self, agent):
  706. agent.write(":UNIT:POW dBm")
  707. class OpticalHeadInterface(Instrument):
  708. "A generic optical head interface"
  709. def initialize(self, agent):
  710. agent.write(":UNIT:POW dBm")
  711. class OpticalSwitch(Instrument):
  712. """A generic optical switch """
  713. class OpticalAttenuator(Instrument):
  714. "A generic optical attenuator"
  715. def _get_attenuation(self):
  716. raw = self.get_agent().ask(":INP:ATT?")
  717. return PhysicalQuantity(SCPIFloat(raw), "dB")
  718. def _set_attenuation(self, atten):
  719. atten = get_quantity(atten, "dB")
  720. self.get_agent().write(":INP:ATT %.2f" % (atten,), wait=True)
  721. #self.get_agent().write(":INP:ATT %.2f" % (atten,), wait=True)
  722. def _no_attenuation(self):
  723. self.get_agent().write(":INP:ATT MIN", wait=True)
  724. attenuation = property(_get_attenuation, _set_attenuation, _no_attenuation, "attenuation factor (dB)")
  725. def _get_max(self):
  726. raw = self.get_agent().ask(":INP:ATT? MAX")
  727. return PhysicalQuantity(SCPIFloat(raw), "dB")
  728. maximum_attenuation = property(_get_max, None, None, "Maximum possible attenuation.")
  729. def _get_min(self):
  730. raw = self.get_agent().ask(":INP:ATT? MIN")
  731. return PhysicalQuantity(SCPIFloat(raw), "dB")
  732. minimum_attenuation = property(_get_min, None, None, "Minimum possible attenuation.")
  733. def _get_wavelength(self):
  734. raw = self.get_agent().ask(":INP:WAV?") # in meters
  735. return PhysicalQuantity(SCPIFloat(raw), "m").inUnitsOf("nm")
  736. def _set_wavelength(self, wl):
  737. wl = get_quantity(wl, "nm")
  738. self.get_agent().write(":INP:WAV %s NM" % (wl._value,), wait=True)
  739. wavelength = property(_get_wavelength, _set_wavelength, None, "wavelengh in nM")
  740. def _get_output(self):
  741. return Boolean(self.get_agent().ask(":OUTP:STAT?"))
  742. def _set_output(self, flag):
  743. flag = Boolean(flag)
  744. self.get_agent().write(":OUTP:STAT %s" % (flag,), wait=True)
  745. output = property(_get_output, _set_output, None, "state of output shutter")
  746. state = output # alias
  747. def on(self):
  748. self._set_output(ON)
  749. def off(self):
  750. self._set_output(OFF)
  751. class OpticalAttenuator_JDSU(Instrument):
  752. "A generic optical attenuator"
  753. def _get_attenuation(self):
  754. addr_params = "%s,%s,%s" % (self.chassis_addr, self.slot_addr, self.device_addr)
  755. cmd = ":OUTP:ATT? %s" % (addr_params)
  756. raw = self.get_agent().ask("%s" % cmd)
  757. return PhysicalQuantity(SCPIFloat(raw), "dB")
  758. def _set_attenuation(self, atten):
  759. atten = get_quantity(atten, "dB")
  760. addr_params = "%s,%s,%s" % (self.chassis_addr, self.slot_addr, self.device_addr)
  761. cmd = ":OUTP:ATT %s,%.2f" % (addr_params, atten)
  762. print "att cmd: %s" % cmd
  763. self.get_agent().write("%s" % (cmd, ),)
  764. time.sleep(1) # JSDU voa need a time delay after set_attenuation
  765. def _no_attenuation(self):
  766. addr_params = "%s,%s,%s" % (self.chassis_addr, self.slot_addr, self.device_addr)
  767. cmd = ":OUTP:ATT %s,%.2f" % (addr_params, 0.0)
  768. self.get_agent().write(cmd, wait=True)
  769. attenuation = property(_get_attenuation, _set_attenuation, _no_attenuation, "attenuation factor (dB)")
  770. def _get_wavelength(self):
  771. addr_params = "%s,%s,%s" % (self.chassis_addr, self.slot_addr, self.device_addr)
  772. raw = self.get_agent().ask(":OUTP:WAV? %s" % addr_params) # in meters
  773. return PhysicalQuantity(SCPIFloat(raw), "m").inUnitsOf("nm")
  774. def _set_wavelength(self, wl):
  775. addr_params = "%s,%s,%s" % (self.chassis_addr, self.slot_addr, self.device_addr)
  776. wl = get_quantity(wl, "nm")
  777. cmd = ":OUTP:WAV %s,%s" % (addr_params,wl._value)
  778. self.get_agent().write("%s" % (cmd,), wait=True)
  779. wavelength = property(_get_wavelength, _set_wavelength, None, "wavelengh in nM")
  780. def _get_output(self):
  781. addr_params = "%s,%s,%s" % (self.chassis_addr, self.slot_addr, self.device_addr)
  782. cmd = ":OUTP:BBLock? %s" % addr_params
  783. return Boolean(self.get_agent().ask(cmd))
  784. def _set_output(self, state):
  785. addr_params = "%s,%s,%s" % (self.chassis_addr, self.slot_addr, self.device_addr)
  786. if state=='ON':
  787. cmd = ":OUTP:BBLock %s,0" % (addr_params)
  788. else:
  789. cmd = ":OUTP:BBLock %s,1" % (addr_params)
  790. self.get_agent().write(cmd, wait=True)
  791. output = property(_get_output, _set_output, None, "state of output shutter")
  792. state = output # alias
  793. def on(self):
  794. self._set_output(ON)
  795. def off(self):
  796. self._set_output(OFF)
  797. # specific instruments
  798. class FIBERAMP(OpticalPowerAmp):
  799. "A PHOTONETICS FIBERAMP BT1400 (EDFA)."
  800. def initialize(self, agent):
  801. agent.write("*SRE=17") # use SRQ line
  802. agent.use_srq()
  803. def _get_value(self, cmd):
  804. a = self.get_agent()
  805. try:
  806. raw = a.ask(cmd)
  807. except visa_exceptions.VisaIOError, err:
  808. if err.error_code == vpp43.VI_ERROR_TMO and a.STB == 3: # invalid command
  809. raise GPIBInvalidCommand, "%s: %s" % (self.visa_resource, cmd)
  810. else:
  811. raise
  812. if raw == "disabled":
  813. raise InstrumentError, "device is disabled"
  814. name, val = raw.split("=",1)
  815. return name, float(val)
  816. def _write(self, cmd):
  817. a = self.get_agent()
  818. a.write(cmd)
  819. a.wait_for_srq()
  820. def _get_current(self):
  821. name, val = self._get_value("I?")
  822. return PhysicalQuantity(val, "mA")
  823. def _set_current(self, mA):
  824. if str(mA).upper().startswith("MAX"):
  825. name, mA = self._get_value("ILIMIT?")
  826. else:
  827. mA = float(get_quantity(mA, "mA"))
  828. self._write("I=%0.1f" % (mA,))
  829. current = property(_get_current, _set_current, None, "constant current mode, current in mA")
  830. def _get_ilimit(self):
  831. name, mA = self._get_value("ILIMIT?")
  832. return PhysicalQuantity(mA, "mA")
  833. max_current = property(_get_ilimit)
  834. def _get_power(self):
  835. name, val = self._get_value("P?")
  836. return PhysicalQuantity(val, "mW")
  837. def _set_power(self, p):
  838. p = get_quantity(p, "mW")
  839. self._write("P=%0.f" % (p._value,))
  840. power = property(_get_power, _set_power, None, "constant power mode, power in mW")
  841. def enable(self):
  842. "Enable output"
  843. self._write("ENABLE")
  844. def disable(self):
  845. "Disable output"
  846. self._write("DISABLE")
  847. def _get_state(self):
  848. raw = self.get_agent().ask("P?")
  849. if raw == "disabled":
  850. return Boolean(0)
  851. else:
  852. return Boolean(1)
  853. def _set_state(self, state):
  854. state = Boolean(state)
  855. if state:
  856. self.enable()
  857. else:
  858. self.disable()
  859. state = property(_get_state, _set_state, None, "operating state")
  860. output = state # alias
  861. def is_locked(self):
  862. "Return state of interlock."
  863. val = self.get_agent().ask("INTERLOCK?")
  864. return Boolean(val)
  865. locked = property(is_locked)
  866. def _get_APC(self):
  867. val = self.get_agent().ask("APC?")
  868. return Boolean(val)
  869. def _set_APC(self, mode):
  870. mode = Boolean(mode)
  871. if mode: # true = APC on
  872. self._write("APCON")
  873. else:
  874. self._write("APCOFF")
  875. def _del_APC(self): # deleting APC is same as setting it off
  876. self._write("APCOFF")
  877. constant_power = property(_get_APC, _set_APC, _del_APC, "constant power (APC) mode control")
  878. def _get_temp(self):
  879. val = self.get_agent().ask("ERRORT?")
  880. return Boolean(val)
  881. temperature_error = property(_get_temp, None, None, "true if there is a Temperature Error condition.")
  882. def _get_ID(self):
  883. a = self.get_agent()
  884. raw = a.ask("*IDN?")
  885. #print "raw %s" %(raw,)
  886. return raw
  887. ID = property(_get_ID, None, None, "Instrument identification")
  888. class KEOPSYSAMP(OpticalPowerAmp):
  889. "A KEOPSYSAMP 18dBm FIBER AMPLIFIER (Model: OI-BT-C-18-sd-B-GP-FA)"
  890. def initialize(self, agent):
  891. agent.write("REM") # set to remote mode
  892. #agent.use_srq()
  893. def _get_value(self, cmd):
  894. a = self.get_agent()
  895. try:
  896. raw = a.ask(cmd)
  897. except visa_exceptions.VisaIOError, err:
  898. if err.error_code == vpp43.VI_ERROR_TMO and a.STB == 3: # invalid command
  899. raise GPIBInvalidCommand, "%s: %s" % (self.visa_resource, cmd)
  900. else:
  901. raise
  902. if raw == "disabled":
  903. raise InstrumentError, "device is disabled"
  904. name, val = raw.split("=",1)
  905. return name, float(val)
  906. def _write(self, cmd):
  907. a = self.get_agent()
  908. a.write(cmd)
  909. #a.wait_for_srq()
  910. def _get_current(self):
  911. name, val = self._get_value("RA1")
  912. print "name %s, val %s" %(name, val)
  913. return PhysicalQuantity(((val/4095) * 2.50)*1000, "mA") #Real value = (nnnn * full sclae) / (4095)
  914. def _set_current(self, mA):
  915. mA = float(get_quantity(mA, "mA"))
  916. mA = mA * 4095 / 2500
  917. self._write("SA1:%04d" % (mA,)) #Nearest integer(nnnn) = (real value * 4095) / fullscale
  918. sleep(2)
  919. current = property(_get_current, _set_current, None, "constant current mode, current in mA")
  920. def _get_power(self):
  921. name, val = self._get_value("RA1")
  922. print "name %s, val %s" %(name, val)
  923. return PhysicalQuantity(((val/4095) * 22.0), "dBm") #Real value = (nnnn * full sclae) / (4095)
  924. def _set_power(self, dbm):
  925. dbm = float(get_quantity(dbm, "dBm"))
  926. dbm = dbm * 4095 / 22.0
  927. self._write("SA1:%d" % (dbm,)) #Nearest integer(nnnn) = (real value * 4095) / fullscale
  928. power = property(_get_power, _set_power, None, "constant output power mode, power in dBm")
  929. def _get_ID(self):
  930. a = self.get_agent()
  931. raw = a.ask("ID")
  932. #print "raw %s" %(raw,)
  933. return raw
  934. ID = property(_get_ID, None, None, "Instrument identification")
  935. def _get_temp(self):
  936. name, val = self._get_value("RA2")
  937. print "name %s, val %s" %(name, val)
  938. return val
  939. temperature = property(_get_temp, None, None, "constant current mode, current in mA")
  940. def enable(self):
  941. "Enable output"
  942. self._write("K1")
  943. def disable(self):
  944. "Disable output"
  945. self._write("K0")
  946. def _set_state(self, state):
  947. state = Boolean(state)
  948. if state:
  949. self.enable()
  950. else:
  951. self.disable()
  952. state = property(_set_state, None, "operating state")
  953. output = state # alias
  954. def set_localmode(self):
  955. "Set to local mode"
  956. self._write("GTL")
  957. class MAP(InstrumentChassis):
  958. SLOTBASE = 1
  959. class HP8163A(InstrumentChassis):
  960. "A HEWLETT-PACKARD HP8163A optical measurement chassis"
  961. SLOTBASE = 1
  962. class HP8163B(InstrumentChassis):
  963. "A HP8163B just inherits HP8163A"
  964. SLOTBASE = 1
  965. class HP81635A(InstrumentModule, OpticalPowerMeter):
  966. "A HEWLETT-PACKARD HP81635A optical power module"
  967. PORTBASE = 1
  968. def get_port(self, port):
  969. return self._get_port(HP81635APort, port)
  970. class HP81635APort(InstrumentPort):
  971. """HP81635A dual power meter for HP8163A chassis"""
  972. MASTER = 1
  973. def initialize(self):
  974. self._currentunit = self._get_unit()
  975. if self._currentunit is None:
  976. raise InstrumentError, "could not initialize HP81635APort"
  977. def _set_unit(self, unit):
  978. assert unit in ("dBm", "DBM", "Watts", "W")
  979. self._agent.write(':SENSE%d:CHAN%d:POW:UNIT %s' % (self.slot, self.port, unit) )
  980. self._currentunit = self._get_unit()
  981. def _get_unit(self):
  982. val = int(self._agent.ask(':SENSE%d:CHAN%d:POW:UNIT?' % (self.slot, self.port) ))
  983. if val == 0:
  984. return "dBm"
  985. elif val == 1:
  986. return "W"
  987. unit = property(_get_unit, _set_unit, None, "Measurement unit: dBm or Watts")
  988. def _set_wavelength(self, wl):
  989. wl = get_quantity(wl, "m")
  990. self._agent.write(':SENSE%d:CHAN%d:POW:WAV %sM' % (self.slot,self.port, wl._value) )
  991. def _get_wavelength(self):
  992. val = self._agent.ask(':SENSE%d:CHAN%d:POW:WAV?' % (self.slot,self.port) )
  993. return PhysicalQuantity(SCPIFloat(val), "m")
  994. wavelength = property(_get_wavelength, _set_wavelength, None, "Wavelength in M")
  995. def _set_averaging(self, tm):
  996. tm = get_quantity(tm, "s")
  997. if self.is_master():
  998. self._agent.write(':SENSE%d:CHAN%d:POW:ATIM %sS' % (self.slot, self.port, tm._value))
  999. else:
  1000. raise InstrumentError, "invalid operation on non-master port"
  1001. def _get_averaging(self):
  1002. val = self._agent.ask(':SENSE%d:CHAN%d:POW:ATIM?' % (self.slot,self.port))
  1003. return PhysicalQuantity(SCPIFloat(val), "s")
  1004. averaging_time = property(_get_averaging, _set_averaging, None, "Averaging time in S")
  1005. def _set_continuous(self, state):
  1006. if self.is_master():
  1007. state = Boolean(state)
  1008. self._agent.write(':INIT%d:CHAN%d:CONT %s' % (self.slot,self.port,state) )
  1009. else:
  1010. raise InstrumentError, "invalid operation on non-master port"
  1011. def _get_continuous(self):
  1012. return Boolean(self._agent.ask(':INIT%d:CHAN%d:CONT?' % (self.slot,self.port)))
  1013. continuous = property(_get_continuous, _set_continuous, None, "continuous measurement mode?")
  1014. def _get_power(self):
  1015. val = self._agent.ask(':FETCH%d:CHAN%d:SCAL:POW?' % (self.slot,self.port) )
  1016. return PhysicalQuantity(SCPIFloat(val), self._currentunit)
  1017. power = property(_get_power, None, None, "Power in current units.")
  1018. class HP81619A(InstrumentModule, OpticalHeadInterface):
  1019. "A HEWLETT-PACKARD HP81635A optical power module"
  1020. PORTBASE = 1
  1021. def get_port(self, port):
  1022. return self._get_port(HP81619APort, port)
  1023. class HP81619APort(HP81635APort):
  1024. "Same as a HP81635APort"
  1025. pass
  1026. class HP81633A(HP81635A):
  1027. "Same as a HP81635A, except with only one port"
  1028. pass
  1029. class HP81633APort(HP81635APort):
  1030. "Same as a HP81635APort"
  1031. pass
  1032. class HP81570A(InstrumentModule, OpticalAttenuator):
  1033. "A HEWLETT-PACKARD HP81635A optical power module"
  1034. PORTBASE = 1
  1035. def get_port(self, port):
  1036. return self._get_port(HP81570APort, port)
  1037. class HP81570APort(HP81635APort):
  1038. "Same as a HP81635APort"
  1039. pass
  1040. class HP81576A(InstrumentModule, OpticalAttenuator, OpticalPowerMeter):
  1041. "A HEWLETT-PACKARD HP81635A optical power module"
  1042. PORTBASE = 1
  1043. def get_port(self, port):
  1044. return self._get_port(HP81635APort, port)
  1045. def _get_power(self):
  1046. self._agent = self.get_agent()
  1047. val = self._agent.ask(':FETCH%d:SCAL:POW?' % (self.slot,))
  1048. #val = self._agent.ask(':FETCH%d:CHAN%d:SCAL:POW?' % (self.slot,self.port))
  1049. return PhysicalQuantity(SCPIFloat(val), self._currentunit)
  1050. power = property(_get_power, None, None, "Power in current units.")
  1051. class HP81576APort(HP81635APort):
  1052. "Same as a HP81635APort"
  1053. pass
  1054. class IA3(InstrumentModule, OpticalAttenuator_JDSU):
  1055. _ATTRS = Instrument._ATTRS.copy()
  1056. _ATTRS.update({
  1057. "chassis_addr" : (str, "1"),
  1058. "slot_addr" : (str, "1"),
  1059. "device_addr" : (str, "1"), # slot maps to list index
  1060. })
  1061. pass
  1062. class IA3Port(InstrumentPort):
  1063. pass
  1064. class OA(IA3):
  1065. pass
  1066. class OAPort(InstrumentPort):
  1067. pass
  1068. class HP81663A(InstrumentModule, OpticalSignalGenerator):
  1069. "A HEWLETT-PACKARD HP81663A optical source"
  1070. PORTBASE = 1
  1071. def initialize(self, agent):
  1072. self.unlock()
  1073. def get_port(self, port=0):
  1074. return self._get_port(HP81663APort, port)
  1075. def _get_wavelength(self):
  1076. from instrumentdata.HP81663A import HP81663_opt2wl
  1077. modopt = self.options()[1]
  1078. return PhysicalQuantity(HP81663_opt2wl[modopt], "nm")
  1079. wavelength = property(_get_wavelength, None, None, "modules wavelength option")
  1080. class HP81662A(HP81663A):
  1081. pass
  1082. class HP81591A(InstrumentModule, OpticalSwitch):# xxx:change type later
  1083. """ Agilent HP 8159A 780 to 1350nm 2x1 Optical Switch """
  1084. PORTBASE = 1
  1085. def get_port(self, port=0):
  1086. return self._get_port(HP8159xPort, port)
  1087. class HP81591APort(InstrumentPort):
  1088. """HP81591A/HP81591B dual power meter for HP8163A chassis"""
  1089. MASTER = 1
  1090. # signal routing functionalities
  1091. def _get_switch_conf(self):
  1092. """
  1093. Queries the switch configuration of the instrument
  1094. """
  1095. # a = self._agent
  1096. a = self.get_agent()
  1097. cmd = ':ROUT%d:CHAN%d:CONF?' % (self.slot, 1)
  1098. print "cmd: %s" % cmd
  1099. val = a.ask(cmd)
  1100. print "val: %s" % val
  1101. return val
  1102. def _get_switch_route(self, chan=1):
  1103. """
  1104. Queries the switch configuration of the instrument
  1105. """
  1106. a = self.get_agent()
  1107. cmd = ':ROUT%d:CHAN%d:CONF:ROUT?' % (self.slot, chan)
  1108. print "cmd: %s" % cmd
  1109. val = a.ask(cmd)
  1110. return val
  1111. def _get_route_chan(self, m=1):
  1112. """
  1113. Queries the current channel route of the switch for a specific module
  1114. and switch channel.
  1115. n: the slot number of the switch module
  1116. m: the switch channel within the selected switch module, default is 1
  1117. """
  1118. #a = self._agent
  1119. a = self.get_agent()
  1120. cmd = ':ROUT%d:CHAN%d?' % (self.slot, m)
  1121. print "cmd: %s" % cmd
  1122. #self.info("xxx", 1)
  1123. val = a.ask(cmd)
  1124. return val
  1125. def _set_route_chan(self, chan='A', m=2):
  1126. """
  1127. Sets the channel route between two ports.
  1128. format:
  1129. :ROUTe[n]:[CHANnel[m]]<wsp><channel_list>
  1130. n: the slot number of the switch module
  1131. m: the switch channel within the selected switch module.
  1132. e.g. for dual 1 x 2 module m = 1 for switch 1; m = 2 for switch 2
  1133. the route between left and right ports.
  1134. channel_list format: [A....Z],[1....n]
  1135. """
  1136. a = self.get_agent()
  1137. chan_map = {1: 2, 2: 1}
  1138. cmd = ':ROUT%d:CHAN%d %s,%d' % (self.slot, chan_map[m], chan, m)
  1139. #print "cmd: %s" % cmd
  1140. val = a.ask(cmd)
  1141. return val
  1142. def do_ask(self, cmd):
  1143. a = self._agent
  1144. #a = self.get_agent()
  1145. #print "cmd: %s" % cmd
  1146. val = a.ask(cmd)
  1147. return val
  1148. def do_write(self, cmd):
  1149. a = self._agent
  1150. #print "cmd: %s" % cmd
  1151. val = a.write(cmd)
  1152. #print "val: %s" % val
  1153. return val
  1154. def set_default_switch(self, chn=1):
  1155. cmd = ":ROUT%d:CHAN%d A,%d" % (chn, chn, chn)
  1156. return self.do_write(cmd)
  1157. def set_switch_for_tx(self):
  1158. cmd = ":ROUT1:CHAN1 A,2"
  1159. return self.do_write(cmd)
  1160. def get_route_config(self, chn=1):
  1161. cmd = ":ROUT%d:CONF:rout?" % chn
  1162. return self.do_ask(cmd)
  1163. def get_port(self, port=0):
  1164. return self._get_port(HP8159xPort, port)
  1165. class HP81591B(InstrumentModule, OpticalSwitch):# xxx:change type later
  1166. """ Agilent HP 8159A 780 to 1350nm 2x1 Optical Switch """
  1167. PORTBASE = 1
  1168. def get_port(self, port=0):
  1169. return self._get_port(HP81591BPort, port)
  1170. class HP81591BPort(InstrumentPort):
  1171. """HP81591A/HP81591B dual power meter for HP8163A chassis"""
  1172. MASTER = 1
  1173. # signal routing functionalities
  1174. def _get_switch_conf(self):
  1175. """
  1176. Queries the switch configuration of the instrument
  1177. """
  1178. a = self._agent
  1179. # a = self.get_agent()
  1180. cmd = ':ROUT%d:CHAN%d:CONF?' % (self.slot, 1)
  1181. print "cmd: %s" % cmd
  1182. val = a.ask(cmd)
  1183. print "val: %s" % val
  1184. return val
  1185. def _get_switch_route(self, chan=1):
  1186. """
  1187. Queries the switch configuration of the instrument
  1188. """
  1189. a = self.get_agent()
  1190. cmd = ':ROUT%d:CHAN%d:CONF:ROUT?' % (self.slot, chan)
  1191. print "cmd: %s" % cmd
  1192. val = a.ask(cmd)
  1193. return val
  1194. def _get_route_chan(self, m=1):
  1195. """
  1196. Queries the current channel route of the switch for a specific module
  1197. and switch channel.
  1198. n: the slot number of the switch module
  1199. m: the switch channel within the selected switch module, default is 1
  1200. """
  1201. a = self._agent
  1202. #a = self.get_agent()
  1203. cmd = ':ROUT%d:CHAN%d?' % (self.slot, m)
  1204. print "cmd: %s" % cmd
  1205. #self.info("xxx", 1)
  1206. val = a.ask(cmd)
  1207. return val
  1208. def _set_route_chan(self, chan='A', m=2):
  1209. """
  1210. Sets the channel route between two ports.
  1211. format:
  1212. :ROUTe[n]:[CHANnel[m]]<wsp><channel_list>
  1213. n: the slot number of the switch module
  1214. m: the switch channel within the selected switch module.
  1215. e.g. for dual 1 x 2 module m = 1 for switch 1; m = 2 for switch 2
  1216. the route between left and right ports.
  1217. channel_list format: [A....Z],[1....n]
  1218. """
  1219. a = self.get_agent()
  1220. chan_map = {1: 2, 2: 1}
  1221. cmd = ':ROUT%d:CHAN%d %s,%d' % (self.slot, chan_map[m], chan, m)
  1222. #print "cmd: %s" % cmd
  1223. val = a.ask(cmd)
  1224. return val
  1225. def do_ask(self, cmd):
  1226. a = self._agent
  1227. #a = self.get_agent()
  1228. #print "cmd: %s" % cmd
  1229. val = a.ask(cmd)
  1230. return val
  1231. def do_write(self, cmd):
  1232. a = self._agent
  1233. #print "cmd: %s" % cmd
  1234. val = a.write(cmd)
  1235. #print "val: %s" % val
  1236. return val
  1237. def set_default_switch(self, chn=1):
  1238. cmd = ":ROUT%d:CHAN%d A,%d" % (chn, chn, chn)
  1239. return self.do_write(cmd)
  1240. def set_switch_for_tx(self):
  1241. cmd = ":ROUT1:CHAN1 A,2"
  1242. return self.do_write(cmd)
  1243. def get_route_config(self, chn=1):
  1244. cmd = ":ROUT%d:CONF:rout?" % chn
  1245. return self.do_ask(cmd)
  1246. def get_port(self, port=0):
  1247. return self._get_port(HP8159xPort, port)
  1248. #class HP81591B(HP81591A):
  1249. # pass
  1250. c