/lib/instruments.py

https://github.com/pruan/TestDepot · Python · 2725 lines · 2074 code · 375 blank · 276 comment · 304 complexity · e41fb248beb7871b14689746d524b862 MD5 · raw 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. class HP81594B(HP81591A):
  1251. # XXX: should really rewrite this function later...
  1252. PORTBASE = 1
  1253. def get_port(self, port=0):
  1254. return self._get_port(HP8159xPort, port)
  1255. def set_default_switch(self):
  1256. """
  1257. set to A
  1258. """
  1259. cmd = "ROUT2:CHAN1 A,1;B,2.A,2;B,1"
  1260. return self.do_write(cmd)
  1261. def set_switch_for_tx(self):
  1262. cmd = ":ROUT2:CHAN1 A,2;B,1.A,1;B,2"
  1263. return self.do_write(cmd)
  1264. #InstrumentModule, OpticalSwitch):# xxx:change type later
  1265. #""" Agilent HP 81594B 780 to 1350nm 2x2 Optical Switch """
  1266. #PORTBASE = 1
  1267. #def get_port(self, port=0):
  1268. # return self._get_port(HP8159xPort, port)
  1269. class HP8159xPort(InstrumentPort):
  1270. """HP81635A dual power meter for HP8163A chassis"""
  1271. MASTER = 1
  1272. def initialize(self):
  1273. self._currentunit = self._get_unit()
  1274. if self._currentunit is None:
  1275. raise InstrumentError, "could not initialize HP8159xPort"
  1276. def _set_unit(self, unit):
  1277. assert unit in ("dBm", "DBM", "Watts", "W")
  1278. self._agent.write(':SENSE%d:CHAN%d:POW:UNIT %s' % (self.slot, self.port, unit) )
  1279. self._currentunit = self._get_unit()
  1280. def _get_unit(self):
  1281. val = int(self._agent.ask(':SENSE%d:CHAN%d:POW:UNIT?' % (self.slot, self.port) ))
  1282. if val == 0:
  1283. return "dBm"
  1284. elif val == 1:
  1285. return "W"
  1286. unit = property(_get_unit, _set_unit, None, "Measurement unit: dBm or Watts")
  1287. def _get_power(self):
  1288. val = self._agent.ask(':FETCH%d:CHAN%d:SCAL:POW?' % (self.slot,self.port) )
  1289. return PhysicalQuantity(SCPIFloat(val), self._currentunit)
  1290. power = property(_get_power, None, None, "Power in current units.")
  1291. class HP81689A(InstrumentModule, OpticalSignalGenerator):
  1292. "A HEWLETT-PACKARD HP81689A optical source"
  1293. PORTBASE = 1
  1294. def get_port(self, port=0):
  1295. return self._get_port(HP81689APort, port)
  1296. class HP816xxAPort(InstrumentPort):
  1297. """The HP81689A laser port.
  1298. Attributes are:
  1299. state - set laser state (on or off)
  1300. lasersource - what laser (selected by wavelength) is to set or query.
  1301. wavelength - set the wavelength to use (LASER_UPPER, LASER_LOWER, LASER_BOTH).
  1302. modulation - set to a Modulation object to modulate the laser according to Modulation.
  1303. power - the power level of the laser
  1304. unit - the default unit for the power setting."""
  1305. def initialize(self):
  1306. self._currentunit = self._get_unit()
  1307. self._lasersource = LASER_LOWER
  1308. def _get_state(self):
  1309. return Boolean(self._agent.ask(':OUTP%d:CHAN%d:STAT?' % (self.slot,self.port)))
  1310. def _set_state(self, st):
  1311. self._agent.write(':OUTP%d:CHAN%d:STAT %s' % (self.slot,self.port, st))
  1312. # set the state to turn laser on or off
  1313. state = property(_get_state, _set_state, None, "Power state of the laser")
  1314. def on(self):
  1315. """Turn laser source on (if it is not locked)."""
  1316. self._set_state(ON)
  1317. def off(self):
  1318. """Turn laser source off."""
  1319. self._set_state(OFF)
  1320. def _get_lasersource(self):
  1321. return self._lasersource
  1322. def _set_lasersource(self, src):
  1323. assert src in (LASER_LOWER, LASER_UPPER), "must set lasersource to LASER_LOWER or LASER_UPPER"
  1324. self._lasersource = src
  1325. lasersource = property(_get_lasersource, _set_lasersource, None, "The laser source on dual source lasers")
  1326. def _get_modulation(self):
  1327. return NotImplemented # XXX
  1328. def _set_modulation(self, mod): # takes a Modulation object parameter
  1329. if issubclass(type(mod), int):
  1330. if mod == OFF:
  1331. self._modulation_off(LASER_LOWER)
  1332. self._modulation_off(LASER_UPPER)
  1333. return
  1334. elif mod == ON:
  1335. return self._agent.write(":SOUR%d:CHAN%d:AM:STAT ON" % (self.slot, self.port)) # XXX
  1336. else:
  1337. raise ValueError, "need ON, OFF, or Modulation object."
  1338. if mod.modtype == DC:
  1339. self._modulation_off(mod.lasersource)
  1340. else:
  1341. self._agent.write(":SOUR%d:CHAN%d:%s:%s:FREQ%d %s" %
  1342. (self.slot, self.port, mod.modtype, mod.source, mod.lasersource, mod.frequency))
  1343. self._agent.write(":SOUR%d:CHAN%d:%s:STAT%d ON" %
  1344. (self.slot, self.port, mod.modtype, mod.lasersource))
  1345. def _modulation_off(self, lasersource):
  1346. self._agent.write(":SOUR%d:CHAN%d:AM:STAT%d OFF" % (self.slot, self.port, lasersource))
  1347. self._agent.write(":SOUR%d:CHAN%d:FM:STAT%d OFF" % (self.slot, self.port, lasersource))
  1348. modulation = property(_get_modulation, _set_modulation, None, "modulation parameters (use Modulation)")
  1349. _UNITMAP = {0:"dBm", 1:"W", 255:None}
  1350. def _get_unit(self):
  1351. val = int(self._agent.ask(':SOUR%d:CHAN%d:POW:UNIT?' % (self.slot, self.port) ))
  1352. if val == 255:
  1353. raise InstrumentError, "Bad unit value. invalid slot?"
  1354. return self._UNITMAP[val]
  1355. def _set_unit(self, unit):
  1356. self._agent.write(':SOUR%d:CHAN%d:POW:UNIT %s' % (self.slot, self.port, unit) )
  1357. self._currentunit = self._get_unit() # verify
  1358. unit = property(_get_unit, _set_unit, None, "Laser power unit: dBm or Watts")
  1359. def _get_power(self):
  1360. val = self._agent.ask(':SOUR%d:CHAN%d:POW?' % (self.slot, self.port) )
  1361. return PhysicalQuantity(SCPIFloat(val), self._currentunit)
  1362. def _set_power(self, pwr):
  1363. pwr = get_quantity(pwr, self._currentunit)
  1364. self._agent.write(':SOUR%d:CHAN%d:POW %s' % (self.slot, self.port, pwr._value) )
  1365. power = property(_get_power, _set_power, None, "Optical power in current unit for current lasersource.")
  1366. class HP81689APort(HP816xxAPort):
  1367. """Tunable laser source."""
  1368. def _get_wavelength(self):
  1369. val = self._agent.ask(":SOUR%d:CHAN%d:WAV:FIXED%d?" % (self.slot, self.port, self._lasersource))
  1370. return PhysicalQuantity(SCPIFloat(val), "m")
  1371. def _set_wavelength(self, wl):
  1372. wl = get_quantity(wl, "m")
  1373. self._agent.write(":SOUR%d:CHAN%d:WAV:FIXED%d %sM" % (self.slot, self.port, self._lasersource, wl._value))
  1374. wavelength = property(_get_wavelength, _set_wavelength, None, "current laser source wavelength")
  1375. class HP81663APort(HP816xxAPort):
  1376. """Distributed Feedback (DFB) laser module, 20 mW."""
  1377. _WAVMAP = {"UPP":LASER_UPPER, "LOW":LASER_LOWER, "BOTH":LASER_BOTH, "ERR":InstrumentError}
  1378. def _get_wavelength(self):
  1379. val = self._agent.ask(":SOUR%d:CHAN%d:POW:WAV?" % (self.slot, self.port))
  1380. return self._WAVMAP[val.strip()]
  1381. def _set_wavelength(self, wl):
  1382. self._agent.write(":SOUR%d:CHAN%d:POW:WAV %s" % (self.slot, self.port, wl))
  1383. self._lasersource = wl
  1384. wavelength = property(_get_wavelength, _set_wavelength, None, "current laser source: LASER_UPPER or LASER_LOWER ")
  1385. class HP86120C(OpticalPowerMeter):
  1386. "A HEWLETT-PACKARD 86120C Multi-Wavelength Meter"
  1387. def _get_array(self):
  1388. a = self.get_agent()
  1389. a.write(":INIT:CONT OFF")
  1390. a.write(":CONF:ARR:POW MAX")
  1391. a.write(":INIT:IMM")
  1392. levels = a.ask(":FETC:ARR:POW?")
  1393. wavelengths = a.ask(":FETC:ARR:POW:WAV?")
  1394. # convert to lists
  1395. levels = filter(None, levels.split(","))
  1396. wavelengths = filter(None, wavelengths.split(","))
  1397. # sanity checks...
  1398. len_levels = int(levels[0]) ; len_wavelengths = int(wavelengths[0])
  1399. assert len_levels == len_wavelengths
  1400. levels.pop(0) ; wavelengths.pop(0)
  1401. assert len_levels == len(levels) ; assert len_wavelengths == len(wavelengths)
  1402. if len_levels > 0:
  1403. return Graph(map(float, wavelengths), "m", map(float, levels), "dBm", "Wavemeter Readings")
  1404. else:
  1405. return None
  1406. waveform = property(_get_array, None, None, "Waveform Graph.")
  1407. measurements = waveform # alias
  1408. class HP8156A(OpticalAttenuator):
  1409. "A HEWLETT-PACKARD HP8156A optical attenuator"
  1410. class HP34401A(DMM):
  1411. """An HEWLETT-PACKARD 34401A"""
  1412. class HP53131A(Instrument):
  1413. """An HEWLETT-PACKARD HP53131A (Frequency Counter) """
  1414. def read_freq(self):
  1415. a = self.get_agent()
  1416. # visa command
  1417. cmd = ":MEASURE:FREQ?"
  1418. print "executing cmd: %s" % cmd
  1419. raw = self.get_agent().ask(cmd)
  1420. freq = PQ(SCPIFloat(raw), "Hz")
  1421. return freq
  1422. class HP86100A(Instrument):
  1423. """An HP86100A"""
  1424. class HP6674A(Instrument):
  1425. """An HP6674A Power Supply"""
  1426. def power_on(self):
  1427. a = self.get_agent()
  1428. # visa command
  1429. cmd = "OUTP ON"
  1430. self.get_agent().write(cmd)
  1431. def power_off(self):
  1432. a = self.get_agent()
  1433. cmd = "OUTP OFF"
  1434. self.get_agent().write(cmd)
  1435. def set_voltage(self, volts):
  1436. """ program voltage level """
  1437. a = self.get_agent()
  1438. cmd = "VOLT %" % volts
  1439. self.get_agent().write(cmd)
  1440. def set_current(self, current):
  1441. a = self.get_agent()
  1442. cmd = "CURR %" % current
  1443. self.get_agent().write(cmd)
  1444. def show_voltage(self):
  1445. cmd = "MEAS:VOLT?"
  1446. self.get_agent().ask(cmd)
  1447. def show_current(self):
  1448. cmd = "MEAS:CURR?"
  1449. self.get_agent().ask(cmd)
  1450. class HP34970A(Instrument):
  1451. """An HP34970A Data Acquisiton/Switch Unit, measure temperature, ac/dc volts, resistance, frequency or current"""
  1452. _ATTRS = {
  1453. "name": (str, "unknown"),
  1454. "manufacturer": (str, "unknown"),
  1455. "modelNumber": (str, "unknown"),
  1456. "serialNumber": (str, "unknown"),
  1457. "firmware": (str, "unknown"),
  1458. "password": (str, "1234"),
  1459. "visa_resource": (str, ""), # e.g.'GPIB2::22'
  1460. "channel": (str, "101"),
  1461. }
  1462. def _get_temp(self, channel=''):
  1463. if (channel):
  1464. self.channel = channel
  1465. cmd = ":MEASURE:TEMPERATURE? TCouple,T,(@%d)" % int(self.channel)
  1466. raw = self.get_agent().ask(cmd)
  1467. return PQ(SCPIFloat(raw), "degC")
  1468. temperature = property(_get_temp)
  1469. def get_temp(self, n=1):
  1470. cmd = ":MEASURE:TEMPERATURE? TCouple,T,(@101"
  1471. for i in range(102,101+n):
  1472. cmd = cmd + ',%d' % (i)
  1473. cmd = cmd + ')'
  1474. tempList = []
  1475. raw = self.get_agent().ask(cmd)
  1476. for temp in raw.split(","):
  1477. tempList.append(PQ(SCPIFloat(temp), "degC"))
  1478. return tempList
  1479. # Scan voltages for all the channels in the scanLst
  1480. # by default the module is assumed to be plugged in slot 1
  1481. def scan_voltages(self, scanLst, slot=1):
  1482. voltDict = {}
  1483. for chn in scanLst :
  1484. voltDict[chn] = self.get_volt(chn,slot).value
  1485. return voltDict
  1486. # Read voltage for the given channel
  1487. def get_volt(self, chn, slot=1):
  1488. cmd = ":MEASURE:VOLTAGE:DC? (@"
  1489. if(slot == 1):
  1490. startChn = 100
  1491. elif(slot == 2):
  1492. startChn = 200
  1493. else:
  1494. startChn = 300
  1495. chn = chn + startChn
  1496. #print "reading DAQ channel: ", chn
  1497. cmd = cmd + '%d' % (chn)
  1498. cmd = cmd + ')'
  1499. tmpLst = []
  1500. raw = self.get_agent().ask(cmd)
  1501. return (PQ(SCPIFloat(raw),"V"))
  1502. class HP33120A(FunctionGenerator):
  1503. """An HP HP33120A"""
  1504. class HP83650L(RFSignalGenerator):
  1505. """Synthesized Swept-CW Generator, 10 MHz to 50 GHz"""
  1506. class AQ6317B(OpticalSpectrumAnalyzer):
  1507. "An ANDO AQ6317B Optical Spectrum Analyzer"
  1508. def initialize(self, agent):
  1509. agent.write("SD0;BD0") # default data delimiters: SD = ',', and BD = CR+LF+EOI
  1510. agent.write("RESCOR 1") # default to RESOLUTION CORRECT
  1511. th2_threshold = 10.0
  1512. print "Setting ENVT2 to %s" % th2_threshold
  1513. agent.write("ENVT2 %s" % th2_threshold)
  1514. def _do_sweep(self):
  1515. a = self.get_agent()
  1516. a.write("STP")
  1517. a.write("SRMSK14") # srq on completed sweep
  1518. a.write("SRQ1")
  1519. a.srquse = True
  1520. a.write("SGL")
  1521. a.wait_for_srq()
  1522. a.srquse = False
  1523. a.write("SRQ0")
  1524. def do_sweep(self):
  1525. """perform a single sweep of the instrument."""
  1526. self._do_sweep()
  1527. _SWEEPMAP = enummap(SWEEP_STOP, SWEEP_SINGLE, SWEEP_CONTINUOUS, SWEEP_AUTO, SWEEP_SEGMENTMEASURE)
  1528. def _get_sweep(self):
  1529. val = self.get_agent().ask('SWEEP?').strip()
  1530. return self._SWEEPMAP[int(val)]
  1531. def _set_sweep(self, swp):
  1532. if swp == SWEEP_STOP:
  1533. self.get_agent().write("STP")
  1534. elif swp == SWEEP_SINGLE:
  1535. self.get_agent().write("SGL")
  1536. elif swp == SWEEP_CONTINUOUS:
  1537. self.get_agent().write("RPT")
  1538. elif swp == SWEEP_AUTO:
  1539. self.get_agent().write("AUTO")
  1540. elif swp == SWEEP_SEGMENTMEASURE:
  1541. self.get_agent().write("SMEAS")
  1542. else:
  1543. raise InstrumentError, "invalid sweep value"
  1544. sweep = property(_get_sweep, _set_sweep, None, "the sweep setting: SWEEP_*")
  1545. def _get_swi(self):
  1546. val = self.get_agent().ask('SWPI?').strip()
  1547. return PQ(int(val), "s")
  1548. def _set_swi(self, val):
  1549. val = get_quantity(val, "s")
  1550. self.get_agent().write('SWPI%s' % (int(val),))
  1551. def _del_swi(self): # deleting sweep interval is the same as setting to zero
  1552. self.get_agent().write('SWPI0')
  1553. sweepInterval = property(_get_swi, _set_swi, _del_swi, "sweep interval in seconds.")
  1554. def _get_swpm(self):
  1555. val = self.get_agent().ask('SWPM?').strip()
  1556. return Boolean(val)
  1557. def _set_swpm(self, val):
  1558. val = Boolean(val)
  1559. self.get_agent().write('SWPM%d' % (int(val),))
  1560. def _del_swpm(self): # deleting sweep marker is the same as turning it off
  1561. self.get_agent().write('SWPM0')
  1562. sweepMarker = property(_get_swpm, _set_swpm, _del_swpm, "marker-to-marker sweep control")
  1563. def _get_center(self):
  1564. if self._get_xunit() == 0:
  1565. raw = self.get_agent().ask('CTRWL?')
  1566. try:
  1567. return PQ(float(raw), "nm")
  1568. except:
  1569. return PQ(0.0, "nm")
  1570. elif self._get_xunit() == 1:
  1571. raw = self.get_agent().ask('CTRF?')
  1572. try:
  1573. return PQ(float(raw), "THz")
  1574. except:
  1575. return PQ(0.0, "THz")
  1576. def _set_center(self, wl):
  1577. if type(wl) is Enum and wl == PEAK:
  1578. self.get_agent().write('CTR=P')
  1579. return
  1580. try:
  1581. wl = get_quantity(wl, "nm")
  1582. except TypeError:
  1583. f = get_quantity(wl, "THz")
  1584. self.get_agent().write('CTRF%3.3f' % (f,))
  1585. else:
  1586. self.get_agent().write('CTRWL%4.2f' % (wl,))
  1587. centerWavelength = property(_get_center, _set_center, None, "center value: wavelength (nm), or PEAK")
  1588. center = centerWavelength # alias
  1589. def _get_start(self):
  1590. if self._get_xunit() == 0:
  1591. raw = self.get_agent().ask('STAWL?')
  1592. return PQ(float(raw), "nm")
  1593. elif self._get_xunit() == 1:
  1594. raw = self.get_agent().ask('STAF?')
  1595. return PQ(float(raw), "THz")
  1596. def _set_start(self, wl):
  1597. try:
  1598. wl = get_quantity(wl, "nm")
  1599. self.get_agent().write('STAWL%4.2f' % (wl,))
  1600. except TypeError:
  1601. f = get_quantity(wl, "THz")
  1602. self.get_agent().write('STAF%3.3f' % (f,))
  1603. startWavelength = property(_get_start, _set_start, None, "Sweep start wavelength")
  1604. start = startWavelength
  1605. def _get_stop(self):
  1606. if self._get_xunit() == 0:
  1607. raw = self.get_agent().ask('STPWL?')
  1608. return PQ(float(raw), "nm")
  1609. elif self._get_xunit() == 1:
  1610. raw = self.get_agent().ask('STPF?')
  1611. return PQ(float(raw), "THz")
  1612. def _set_stop(self, wl):
  1613. try:
  1614. wl = get_quantity(wl, "nm")
  1615. self.get_agent().write('STPWL%4.2f' % (wl,))
  1616. except TypeError:
  1617. f = get_quantity(wl, "THz")
  1618. self.get_agent().write('STPF%3.3f' % (f,))
  1619. stopWavelength = property(_get_stop, _set_stop, None, "Sweep stop wavelength")
  1620. stop = stopWavelength
  1621. def _get_RESCOR(self):
  1622. stat = int(self.get_agent().ask('RESCOR?'))
  1623. return stat
  1624. def _set_RESCOR(self, state):
  1625. self.get_agent().write("RESCOR %s" % state)
  1626. self._cache["_rescor"] = int(self.get_agent().ask('RESCOR?'))
  1627. rescor = property(_get_RESCOR, _set_RESCOR, None, "RESOLUNTION CORRECT: 0/1")
  1628. def _get_X(self):
  1629. xunt = int(self.get_agent().ask('XUNT?'))
  1630. self._cache["_xunt"] = xunt
  1631. if xunt == 0:
  1632. return SCALE_WAVELENGTH
  1633. elif xunt == 1:
  1634. return SCALE_FREQUENCY
  1635. def _set_X(self, scale):
  1636. if scale not in (SCALE_WAVELENGTH , SCALE_FREQUENCY):
  1637. raise InstrumentError, "invalid scale unit"
  1638. self.get_agent().write('XUNT %d' % (scale,))
  1639. self._cache["_xunt"] = int(self.get_agent().ask('XUNT?'))
  1640. scaleX = property(_get_X, _set_X, None, "X scale units: SCALE_WAVELENGTH , SCALE_FREQUENCY")
  1641. def _get_xunit(self):
  1642. try:
  1643. return self._cache["_xunt"]
  1644. except KeyError:
  1645. self._get_X()
  1646. return self._cache["_xunt"]
  1647. def _get_span(self):
  1648. if self._get_xunit() == 0:
  1649. raw = self.get_agent().ask('SPAN?')
  1650. return PQ(float(raw), "nm")
  1651. elif self._get_xunit() == 1:
  1652. raw = self.get_agent().ask('SPANF?')
  1653. return PQ(float(raw), "THz")
  1654. def _set_span(self, span):
  1655. try:
  1656. span = get_quantity(span, "nm")
  1657. assert float(span) <= 1200.0
  1658. self.get_agent().write('SPAN%4.1f' % (span,))
  1659. except TypeError:
  1660. span = get_quantity(span, "THz")
  1661. assert float(span) <= 350.0
  1662. self.get_agent().write('SPANF%3.3f' % (span,))
  1663. def _del_span(self, span):
  1664. self.get_agent().write('SPAN0')
  1665. span = property(_get_span, _set_span, _del_span, "scan span in nm")
  1666. def _get_ref(self):
  1667. raw = self.get_agent().ask('REFL?')
  1668. if raw.startswith("PW"):
  1669. return PQ(float(raw[2:]), "pW")
  1670. elif raw.startswith("NW"):
  1671. return PQ(float(raw[2:]), "nW")
  1672. elif raw.startswith("UW"):
  1673. return PQ(float(raw[2:]), "muW")
  1674. elif raw.startswith("MW"):
  1675. return PQ(float(raw[2:]), "mW")
  1676. else:
  1677. return PQ(float(raw), "dBm")
  1678. def _set_ref(self, val):
  1679. if type(val) is Enum and val == PEAK:
  1680. self.get_agent().write('REF=P')
  1681. return
  1682. if type(val) is str:
  1683. val = PhysicalQuantity(val)
  1684. uname = val.unit.name()
  1685. if uname == "dBm":
  1686. self.get_agent().write('REFL%-4.1f' % (val,))
  1687. elif uname == "pW":
  1688. self.get_agent().write('REFLP%-4.1f' % (val,))
  1689. elif uname == "nW":
  1690. self.get_agent().write('REFLN%-4.1f' % (val,))
  1691. elif uname == "muW":
  1692. self.get_agent().write('REFLU%-4.1f' % (val,))
  1693. elif uname == "mW":
  1694. self.get_agent().write('REFLM%-4.1f' % (val,))
  1695. else:
  1696. raise ValueError, "invalid reference power"
  1697. referenceLevel = property(_get_ref, _set_ref, None, "Reference level")
  1698. reference = referenceLevel
  1699. def _get_res(self):
  1700. if self._get_xunit() == 0:
  1701. raw = self.get_agent().ask('RESLN?')
  1702. return PQ(float(raw), "nm")
  1703. elif self._get_xunit() == 1:
  1704. raw = self.get_agent().ask('RESLNF?')
  1705. return PQ(float(raw), "GHz")
  1706. def _set_res(self, res):
  1707. if self._get_xunit() == 0:
  1708. res = get_quantity(res, "nm")
  1709. assert float(res) <= 2.0
  1710. self.get_agent().write('RESLN%.2f' % (res._value,))
  1711. elif self._get_xunit() == 1:
  1712. res = get_quantity(res, "GHz")
  1713. self.get_agent().write('RESLNF%.0f' % (res._value,))
  1714. resolution = property(_get_res, _set_res, None, "resolution: 0.01 to 2.0 nm or 2 to 400 GHz")
  1715. def _get_sampl(self):
  1716. raw = self.get_agent().ask('SMPL?')
  1717. return int(raw)
  1718. def _set_sampl(self, val):
  1719. val = int(val)
  1720. assert val <= 200001 and val >= 11
  1721. self.get_agent().write('SMPL%d' % (val,))
  1722. def _del_sampl(self):
  1723. self.get_agent().write('SMPL0')
  1724. samplePoint = property(_get_sampl, _set_sampl, _del_sampl, "sampling point: 11 to 20001. delete to set auto")
  1725. # markers and analysis
  1726. def _set_lmarkerx(self, num, wl):
  1727. wl = get_quantity(wl, "nm")
  1728. self.get_agent().write("L%dMK%.3f" % (num, wl))
  1729. def _get_lmarkerx(self, num):
  1730. raw = self.get_agent().ask("L%dMK?" % (num,))
  1731. if raw:
  1732. return PhysicalQuantity(float(raw), "nm")
  1733. else:
  1734. return None
  1735. linemarker1 = property(lambda s: s._get_lmarkerx(1),
  1736. lambda s, v: s._set_lmarkerx(1, v), "Line marker 1 (left)")
  1737. linemarker2 = property(lambda s: s._get_lmarkerx(2),
  1738. lambda s, v: s._set_lmarkerx(2, v), "Line marker 2 (right)")
  1739. def clear_linemarkers(self):
  1740. self.get_agent().write("LMKCL")
  1741. def _set_levmarkerx(self, num, dbm):
  1742. dbm = get_quantity(dbm, "dBm")
  1743. self.get_agent().write("L%dDBM%.2f" % (num, dbm))
  1744. def _get_levmarkerx(self, num):
  1745. raw = self.get_agent().ask("L%dMK?" % (num,))
  1746. if raw:
  1747. return PhysicalQuantity(float(raw), "dBm")
  1748. else:
  1749. return None
  1750. levelmarker1 = property(lambda s: s._get_levmarkerx(3),
  1751. lambda s, v: s._set_levmarkerx(3, v), "Level marker 1 (top)")
  1752. levelmarker2 = property(lambda s: s._get_levmarkerx(4),
  1753. lambda s, v: s._set_levmarkerx(4, v), "Level marker 2 (bottom)")
  1754. def clear_markers(self):
  1755. self.get_agent().write("MKCL")
  1756. def _set_marker(self, wl):
  1757. if type(wl) is Enum:
  1758. if wl == CENTER:
  1759. self.get_agent().write('CTR=M')
  1760. elif wl == REFERENCE:
  1761. self.get_agent().write('REF=M')
  1762. return
  1763. try:
  1764. wl = get_quantity(wl, "nm")
  1765. self.get_agent().write("WMKR%.3f" % (wl,))
  1766. except TypeError:
  1767. f = get_quantity(wl, "THz")
  1768. self.get_agent().write("FMKR%.3f" % (f,))
  1769. def _get_marker(self):
  1770. raw = self.get_agent().ask("MKR?")
  1771. if raw:
  1772. rwl, rlev = raw.split(",", 1)
  1773. xu = IF(self._get_xunit(), "THz", "nm")
  1774. return PhysicalQuantity(float(rwl), xu), PhysicalQuantity(float(rlev), "dBm") # XXX level?
  1775. else:
  1776. return None, None
  1777. marker = property(_get_marker, _set_marker, clear_markers, "Moving marker")
  1778. # alternate method call interface for getting/setting markers by index number
  1779. def set_marker(self, n, val):
  1780. if n in (1,2):
  1781. return self._set_lmarkerx(n, val)
  1782. elif n in (3,4):
  1783. return self._set_levmarkerx(n, val)
  1784. def get_marker(self, n):
  1785. if n in (1,2):
  1786. return self._get_lmarkerx(n)
  1787. elif n in (3,4):
  1788. return self._get_levmarkerx(n)
  1789. # using fixed markers
  1790. def set_fixed_marker(self, n, wl=None):
  1791. n = int(n)+4
  1792. if wl is not None:
  1793. self._set_marker(wl)
  1794. self.get_agent().write("MKR%d" % (n,))
  1795. def get_fixed_marker(self, n):
  1796. n = int(n)+4
  1797. raw = self.get_agent().ask("MKR?%d" % (n,))
  1798. if raw:
  1799. rwl, rlev = raw.split(",", 1)
  1800. return (PhysicalQuantity(float(rwl), "nm"), PhysicalQuantity(float(rlev), "dBm"))
  1801. else:
  1802. return None
  1803. def clear_fixed_marker(self, n):
  1804. n = int(n)+4
  1805. self.get_agent().write("MCLR%d" % (n,))
  1806. def get_OSNR(self, wl=None):
  1807. """Returns an OSNR of an individual channel."""
  1808. if self._get_xunit() != 0:
  1809. self.scaleX = SCALE_WAVELENGTH
  1810. if wl is None:
  1811. wl = self._get_center()
  1812. else:
  1813. self._set_center(wl)
  1814. self._set_span("5.0 nm")
  1815. self._set_res("1.0 nm")
  1816. self._do_sweep()
  1817. self.get_agent().write('CTR=P') # peak to center
  1818. wl = self._get_center()
  1819. self.set_fixed_marker(1, wl)
  1820. self.set_fixed_marker(2, wl-PQ(1.5, "nm")) # XXX: 1.5 for DPSK
  1821. self.set_fixed_marker(3, wl+PQ(1.5, "nm"))
  1822. Ps = 10**(float(self.get_fixed_marker(1)[1])/10.0) # in mW
  1823. nflow = 10.0**(float(self.get_fixed_marker(2)[1])/10.0) # in mW
  1824. nfhi = 10.0**(float(self.get_fixed_marker(3)[1])/10.0) # in mW
  1825. Pn = (nflow + nfhi)/2.0
  1826. osnr = round((10.0*math.log10((Ps-Pn)/Pn) + 10.0), 3) #corr fact 10dB for 1.0nm res DPSK
  1827. return PQ(osnr, "dB")
  1828. OSNR = property(get_OSNR)
  1829. def get_peak(self):
  1830. # start peak search
  1831. self.get_agent().write('PKSR')
  1832. self.get_agent().write('CTR=P') # peak to center
  1833. pwl = self._get_center()
  1834. return pwl
  1835. PEAK = property(get_peak)
  1836. def get_avgOSNR(self, wllist):
  1837. """Returns average and individual OSNR of multiple channels."""
  1838. pwllist = []
  1839. osnrdict = {}
  1840. minwl = min(wllist)
  1841. maxwl = max(wllist)
  1842. cwl = avg(wllist)
  1843. self._set_center(cwl)
  1844. if self._get_xunit() != 0:
  1845. self.scaleX = SCALE_WAVELENGTH
  1846. if len(wllist) == 1: # for single wavelength
  1847. self._set_span("5.0 nm")
  1848. self._set_res("0.5 nm")
  1849. self._do_sweep()
  1850. self.get_agent().write('CTR=P') # peak to center
  1851. pwl = self._get_center()
  1852. pwllist.append(pwl)
  1853. else: # for multiple wavelength
  1854. span = (maxwl - minwl) + PQ(2.0, "nm") #adding margin of 2nm
  1855. self._set_span(span)
  1856. self._set_res("0.1 nm")
  1857. self._do_sweep()
  1858. self.get_agent().write('MKCL') # clear marker
  1859. self.get_agent().write('PKSR') # start peak search
  1860. self.set_fixed_marker(1, (minwl - PQ(0.5, "nm")))
  1861. for wl in wllist:
  1862. self.get_agent().write('NSRR') # next search
  1863. pwl, plev = self.marker
  1864. pwllist.append(pwl)
  1865. self._set_res("0.5 nm")
  1866. self._do_sweep()
  1867. pwllist.sort()
  1868. for wl, pwl in zip(wllist, pwllist):
  1869. #print "wl %s, pwl %s" %(wl, pwl)
  1870. self.set_fixed_marker(1, pwl)
  1871. self.set_fixed_marker(2, pwl-PQ(0.75, "nm"))
  1872. self.set_fixed_marker(3, pwl+PQ(0.75, "nm"))
  1873. Ps = 10**(float(self.get_fixed_marker(1)[1])/10.0) # in mW
  1874. nflow = 10.0**(float(self.get_fixed_marker(2)[1])/10.0) # in mW
  1875. nfhi = 10.0**(float(self.get_fixed_marker(3)[1])/10.0) # in mW
  1876. Pn = (nflow + nfhi)/2.0
  1877. osnrdict[wl] = PQ(round((10.0*math.log10((Ps-Pn)/Pn) + 7.0), 3), "dB") #corr fact 7dB for 0.5nm res
  1878. avgosnr = avg(osnrdict.values())
  1879. return avgosnr, osnrdict
  1880. def get_avgSigPwr(self, wllist):
  1881. """Returns average and individual signal power of multiple channels."""
  1882. pwllist = []
  1883. plevdict = {}
  1884. osnrdict = {}
  1885. minwl = min(wllist)
  1886. maxwl = max(wllist)
  1887. cwl = avg(wllist)
  1888. self._set_center(cwl)
  1889. if self._get_xunit() != 0:
  1890. self.scaleX = SCALE_WAVELENGTH
  1891. if len(wllist) == 1: # for single wavelength
  1892. self._set_span("5.0 nm")
  1893. self._set_res("0.5 nm")
  1894. self._do_sweep()
  1895. self.get_agent().write('CTR=P') # peak to center
  1896. pwl = self._get_center()
  1897. pwllist.append(pwl)
  1898. else: # for multiple wavelength
  1899. span = (maxwl - minwl) + PQ(2.0, "nm") #adding margin of 2nm
  1900. self._set_span(span)
  1901. self._set_res("0.1 nm")
  1902. self._do_sweep()
  1903. self.get_agent().write('MKCL') # clear marker
  1904. self.get_agent().write('PKSR') # start peak search
  1905. self.referenceLevel = PEAK #set REFLEV to PEAK
  1906. self.set_fixed_marker(1, (minwl - PQ(1.0, "nm")))
  1907. for wl in wllist:
  1908. self.get_agent().write('NSRR') # next search
  1909. pwl, plev = self.marker
  1910. plevdict[wl] = plev
  1911. pwllist.append(pwl)
  1912. self._set_res("0.5 nm")
  1913. pwllist.sort()
  1914. for wl, pwl in zip(wllist, pwllist):
  1915. print "wl %s, pwl %s, plev %s" %(wl, pwl, plevdict[wl])
  1916. avgplev = avg(plevdict.values())
  1917. if len(wllist) == 1:
  1918. return avgplev
  1919. return avgplev, plevdict
  1920. def get_spectralwidth(self, xunit=None, bw=None, wl=None):
  1921. if self._get_xunit() != 0:
  1922. self.scaleX = SCALE_WAVELENGTH
  1923. if wl is None:
  1924. wl = self.center
  1925. else:
  1926. self.center = wl
  1927. self.span = "5.0 nm"
  1928. self.resolution = "0.02 nm"
  1929. if xunit is None:
  1930. self.scaleX = SCALE_WAVELENGTH
  1931. else:
  1932. self.scaleX = SCALE_FREQUENCY
  1933. self.sweep = SWEEP_SINGLE
  1934. sleep(10)
  1935. self.get_agent().write('SW0') #envelope
  1936. if bw is None:
  1937. self.get_agent().write('SWENV%.2f' %(3.0,)) # 3dB bandwidth
  1938. else:
  1939. self.get_agent().write('SWENV%.2f' %(float(bw),)) #
  1940. sleep(2)
  1941. raw = self.get_agent().ask("ANA?") # analysis functionality
  1942. self.scaleX = SCALE_WAVELENGTH # default state
  1943. self.sweep = SWEEP_CONTINUOUS
  1944. if raw:
  1945. rlist = raw.split(",")
  1946. #print "rlist %s" %rlist
  1947. wl, sw = rlist[0], rlist[1]
  1948. #print "wl %s, bw %s" %(wl, sw)
  1949. if xunit is None:
  1950. unit = "nm"
  1951. else:
  1952. unit = xunit
  1953. return PQ(float(wl), unit), PQ(float(sw), unit)
  1954. else:
  1955. return None
  1956. spectralwidth = property(get_spectralwidth)
  1957. # tracing operations...
  1958. def _get_trc(self, abc, wl=None):
  1959. if self._get_xunit() != 0:
  1960. self.scaleX = SCALE_WAVELENGTH
  1961. if wl is None:
  1962. wl = self.center
  1963. else:
  1964. self.center = wl
  1965. self.span = "2.5 nm"
  1966. self.resolution = "0.01 nm"
  1967. self.sweep = SWEEP_SINGLE
  1968. sleep(10)
  1969. levels = self.get_agent().ask("LDAT%s" % (abc,))
  1970. wavelengths = self.get_agent().ask("WDAT%s" % (abc,))
  1971. self.sweep = SWEEP_CONTINUOUS
  1972. levels = levels.split(",")
  1973. wavelengths = wavelengths.split(",")
  1974. data = []
  1975. for wl, level in zip(wavelengths, levels):
  1976. data.append((float(wl), float(level)))
  1977. # sanity checks...
  1978. len_levels = int(levels[0]) ; len_wavelengths = int(wavelengths[0])
  1979. assert len_levels == len_wavelengths
  1980. levels.pop(0) ; wavelengths.pop(0)
  1981. assert len_levels == len(levels)
  1982. assert len_wavelengths == len(wavelengths)
  1983. return Graph(map(float, wavelengths), "nm", map(float, levels), "dBm", "AQ6317B trace%s" % (abc,)), data
  1984. traceA = property(lambda s: s._get_trc("A"), None, None, "trace A waveform Graph.")
  1985. traceB = property(lambda s: s._get_trc("B"), None, None, "trace B waveform Graph.")
  1986. traceC = property(lambda s: s._get_trc("C"), None, None, "trace C waveform Graph.")
  1987. waveform = traceA # alias
  1988. class AQ6370(AQ6317B):
  1989. """ just inherit and hope for the best """
  1990. pass
  1991. class GP700(Instrument):#InstrumentChasiss):
  1992. #class GP700(Instrument):
  1993. """ dicon GP700 Optical SW """
  1994. _SWITCHMAP = {
  1995. -700 : 2,
  1996. -50 : 3,
  1997. 50 : 4,
  1998. 700 : 5,
  1999. 0 : 1,
  2000. }
  2001. _INVERSEMAP = dict(map(lambda t: (t[1], t[0]), _SWITCHMAP.items()))
  2002. def _get_sys_conf(self):
  2003. """
  2004. Queries the switch configuration of the instrument, returns dictionary
  2005. """
  2006. dev = {}
  2007. a = self.get_agent()
  2008. cmd = 'SYST:CONF?'
  2009. val = a.ask(cmd)
  2010. devices = val.split(',')
  2011. for device in devices:
  2012. info = device.lstrip().split(' ')
  2013. if len(info) == 1:
  2014. dev[info[0]] = None
  2015. elif len(info) == 2:
  2016. dev[info[0]] = info[1]
  2017. elif len(info) == 3:
  2018. key = info[0][0] + info[0][2]
  2019. dev[key] = info[1] + ' ' + info[2]
  2020. else:
  2021. dev[device[0]] = None
  2022. return dev
  2023. def _set_switch(self, sw_type, output, input=None):
  2024. # first verify is the the host have the module type plugged in
  2025. sys_config = self._get_sys_conf()
  2026. assert sys_config.has_key(sw_type), "Invalid SW type"
  2027. maxchan = sys_config[sw_type]
  2028. if maxchan.startswith('MAX'):
  2029. max_chan = int(maxchan.split('MAXCHAN')[1])
  2030. if int(output) > max_chan:
  2031. print "Output :%s setting is greater than max of %d" % (output,
  2032. max_chan)
  2033. return False
  2034. cmd = "%s %s" % (sw_type, output)
  2035. #print "Setting %s to output %s..." % (sw_type, output)
  2036. a = self.get_agent()
  2037. a.write(cmd)
  2038. sw_status = a.ask("%s?" % sw_type)
  2039. try:
  2040. sw_state = sw_status.split(',')[0]
  2041. except:
  2042. sw_state = sw_status
  2043. #print "switch %s is in state: %s" % (sw_type, sw_state)
  2044. if eval(sw_state) == output:
  2045. return True
  2046. else:
  2047. return False
  2048. # P1 port1/2 S1/2/3):
  2049. def set_switch(self, mod_type, output, sw_num=None):
  2050. """
  2051. generic switch interface
  2052. mod_type: "M" or "P", follow by the slot number
  2053. sw_num: The switch within the module
  2054. output: The port to which the signal is routed.
  2055. """
  2056. if type(mod_type) is int:
  2057. # for backward compatiblity this takes the form:
  2058. # set_switch(6, 'cnt') mod_type is actually the switch number,
  2059. # output is actually the state, 'byp' or 'cnt'
  2060. if (output == 'cnt'):
  2061. sw_pos = 0
  2062. else:
  2063. sw_pos = 1
  2064. output = mod_type
  2065. self._set_switch('M1', sw_pos)
  2066. self._set_switch('M2', sw_pos)
  2067. else:
  2068. if mod_type.startswith('M'):
  2069. # for M type module, setting the output is simple
  2070. self._set_switch(mod_type, output)
  2071. else: # a P type module.
  2072. # step 1: get the current sw_val to write to.
  2073. current_sw_val = int(self.get_switch(mod_type))
  2074. # step 2: do math translate sw val
  2075. new_sw_val = get_new_val(sw_num, output, current_sw_val)
  2076. self._set_switch(mod_type, new_sw_val)
  2077. def get_switch(self, sw_type):
  2078. a = self.get_agent()
  2079. return a.ask("%s?" % sw_type)
  2080. def set_4x1_switch_output(self, p_num, input_num):
  2081. """
  2082. We make a 4x1 switch by using 3 2x1 switch. We impose that the switch
  2083. must be configured as follow:
  2084. 1 --\
  2085. P2 }---\
  2086. 2 --/ --\
  2087. P1 }----
  2088. 3 --\ --/
  2089. P3 }---/
  2090. 4 --/
  2091. p_num is the name of the P-module within the DiCon chassis
  2092. input_num is the input that the we want to connect to the output of the
  2093. 4x1 switch (Px:1)
  2094. """
  2095. if (input_num % 4) == 1: #
  2096. # do necessary setup to have output = input#1
  2097. print "Input is SW #1"
  2098. self.set_switch(p_num, 1, 1)
  2099. self.set_switch(p_num, 1, 2)
  2100. elif (input_num % 4) == 2:
  2101. # do necessary setup to have output = input#2
  2102. print "Input is SW #2"
  2103. self.set_switch(p_num, 1, 1)
  2104. self.set_switch(p_num, 2, 2)
  2105. elif (input_num % 4) == 3:
  2106. # do necessary setup to have output = input#3
  2107. print "Input is SW #3"
  2108. self.set_switch(p_num, 2, 1)
  2109. self.set_switch(p_num, 1, 3)
  2110. else: # input_num is 4
  2111. print "Input is SW #4"
  2112. self.set_switch(p_num, 2, 1)
  2113. self.set_switch(p_num, 2, 3)
  2114. dBox = property(lambda self: self)
  2115. # dispersion mapping for Dicon M modules (8x1) to emulate in-house ProgCD
  2116. """
  2117. 2 : -700
  2118. 3 : -50
  2119. 4 : 50
  2120. 5 : 700
  2121. 1 : 0
  2122. """
  2123. def _get_dispersion(self):
  2124. res = self.get_switch('M1')
  2125. sw_pos = int(res.split(',')[0])
  2126. dispersion = self._INVERSEMAP[sw_pos]
  2127. return dispersion
  2128. def _set_dispersion(self, disp):
  2129. sw_pos = self._SWITCHMAP[disp]
  2130. self._set_switch('M1', sw_pos)
  2131. self._set_switch('M2', sw_pos)
  2132. dispersion = property(_get_dispersion, _set_dispersion, None, "Dispersion value")
  2133. # efficient storage of graphs with units. Iterator over datapoints, indexing
  2134. # returns physcial values.
  2135. class Graph(object):
  2136. def __init__(self, xinit, xunit, yinit, yunit, title=None):
  2137. import array
  2138. self._xunit = xunit
  2139. self._yunit = yunit
  2140. self._x = array.array("d", xinit)
  2141. self._y = array.array("d", yinit)
  2142. self._i = 0
  2143. self.title = title or self.__class__.__name__
  2144. def __str__(self):
  2145. return "%s with %d datapoints, X unit is %r, Y unit is %r )" \
  2146. % (self.title, len(self._x), self._xunit, self._yunit)
  2147. def plot(self, ui):
  2148. winx, winy = ui.winsize
  2149. ui.Print(self.title)
  2150. miny = min(self._y)
  2151. maxy = max(self._y)
  2152. maxx = max(self._x)
  2153. minx = min(self._x)
  2154. ui.Print("X range (%s, %s), Y range (%s, %s)." % (minx, maxx, miny, maxy))
  2155. sx = max(1, int(len(self._x)/(winy-3)))
  2156. sy = (winx-9)/max(1, (maxy-miny))
  2157. for i in range(0, len(self._x), sx):
  2158. x = self._x[i]
  2159. py = int((self._y[i]-miny)*sy)
  2160. ui.Print("%04.2f:%s+" % (x, " "*py))
  2161. ui.Print(" %04.2f%s%04.2f" % (miny, " "*(winx-22), maxy))
  2162. def __getitem__(self, idx):
  2163. return PQ(self._x[idx], self._xunit), PQ(self._y[idx], self._yunit)
  2164. def __iter__(self):
  2165. self._i = 0
  2166. return self
  2167. def __len__(self):
  2168. return len(self._x)
  2169. def next(self):
  2170. try:
  2171. x = self._x[self._i]
  2172. y = self._y[self._i]
  2173. except IndexError:
  2174. raise StopIteration
  2175. self._i += 1
  2176. return x, y
  2177. xunit = property(lambda s: s._xunit)
  2178. yunit = property(lambda s: s._yunit)
  2179. # convert a PhysicalQuantity (as object or string) to given unit value.
  2180. def get_quantity(obj, unit):
  2181. if type(obj) is str:
  2182. obj = PhysicalQuantity(obj)
  2183. return obj.inUnitsOf(unit)
  2184. # Instrument control objects
  2185. class Marker(object):
  2186. """Generic marker for sweeping instruments."""
  2187. def __init__(self, number, value=None):
  2188. self.number = int(number)
  2189. self.value = value
  2190. def __str__(self):
  2191. return "Marker %d: %s" % (self.number, self.value)
  2192. def __int__(self):
  2193. return self.number
  2194. class Modulation(object):
  2195. """Holds modulation parameters for general use. """
  2196. def __init__(self, mtype, freq, amplitude=None, bandwidth=None, source=INT, lasersource=LASER_LOWER):
  2197. self.modtype = mtype
  2198. self.frequency = PhysicalQuantity(SCPIFloat(freq), "Hz")
  2199. self.amplitude = amplitude
  2200. self.bandwidth = bandwidth
  2201. self.source = source
  2202. self.lasersource = lasersource
  2203. def __str__(self):
  2204. if self.modtype == AM:
  2205. return "Modulation: %s freq:%s amp:%s" % (self.modtype, self.frequency, self.amplitude)
  2206. elif self.modtype == FM:
  2207. return "Modulation: %s freq:%s bw:%s" % (self.modtype, self.frequency, self.bandwidth)
  2208. else:
  2209. return "Modulation: %s freq:%s amp:%s bw:%s" % (self.modtype, self.frequency, self.amplitude, self.bandwidth)
  2210. class BitField(object):
  2211. """
  2212. This class allow user to mainipulate each bit field individually
  2213. """
  2214. def __init__(self,value=0):
  2215. self._d = value
  2216. def __getitem__(self, index):
  2217. return (self._d >> index) & 1
  2218. def __setitem__(self,index,value):
  2219. value = (value&1L)<<index
  2220. mask = (1L)<<index
  2221. self._d = (self._d & ~mask) | value
  2222. def __getslice__(self, start, end):
  2223. mask = 2L**(end - start) -1
  2224. return (self._d >> start) & mask
  2225. def __setslice__(self, start, end, value):
  2226. mask = 2L**(end - start) -1
  2227. value = (value & mask) << start
  2228. mask = mask << start
  2229. self._d = (self._d & ~mask) | value
  2230. return (self._d >> start) & mask
  2231. def __int__(self):
  2232. return self._d
  2233. def get_new_val(sw_num, port, sw_val):
  2234. k = BitField()
  2235. h = BitField()
  2236. k[0:3] = sw_val - 1
  2237. h[0:3] = sw_val - 1
  2238. bo = int(k)
  2239. #h[sw_num-1] = port
  2240. h[sw_num-1] = port - 1
  2241. # new value
  2242. new_sw_val = int(h) + 1
  2243. #print "%d, %d, %d %d %d %d %d %d \t%d" % (port, sw_val, k[2], k[1],
  2244. # k[0], h[2], h[1], h[0], bn)
  2245. return new_sw_val
  2246. # scans the VISA instrument list and updates the devstorage with Instrument objects.
  2247. def update_instruments(devstorage, gpib_name=None, iostream=sys.stderr):
  2248. #def update_instruments(devstorage, iostream=sys.stderr):
  2249. rv = []
  2250. insts = visa.get_instruments_list(False)
  2251. if gpib_name is None:
  2252. print "Scanning the entire GPIB network..."
  2253. else:
  2254. insts = filter(lambda names: names.startswith(gpib_name), insts)
  2255. if len(insts) == 0:
  2256. print "Can't find specified GPIB resource %s, scanning the entire GPIB network..." % gpib_name
  2257. insts = visa.get_instruments_list(False)
  2258. for addr in insts:
  2259. try:
  2260. iostream.write("Trying: %s\n" % (addr,)) ; iostream.flush()
  2261. inst = visa.instrument(addr)
  2262. inst.write("*IDN?")
  2263. raw = inst.read()
  2264. except visa_exceptions.VisaIOError:
  2265. iostream.write("Warning: could not connect to %r.\n" % addr)
  2266. continue
  2267. if raw:
  2268. try:
  2269. [manufacturer, model, serial, firmware] = raw.split(",", 3)
  2270. model = model.replace(" ", "")
  2271. cls = get_class(model)
  2272. if cls is None:
  2273. iostream.write("Warning: No instrument object for model %r.\n" % model)
  2274. continue
  2275. #print "--- calling _add_inst() "
  2276. name = _add_inst(devstorage, cls, addr, model, serial)
  2277. if name:
  2278. iostream.write("Added: %s\n" % (name,)) ; iostream.flush()
  2279. rv.append(name)
  2280. except ValueError:
  2281. pass
  2282. return rv
  2283. def _add_inst(d, cls, name, model, serial):
  2284. visa_resource = name
  2285. rsrc = GPIBResource()
  2286. rsrc.parse(name)
  2287. name = "%s_gpib%s_%s" % (model, rsrc.board, rsrc.address)
  2288. inst = cls(name=name)
  2289. inst.visa_resource = visa_resource
  2290. #print "updating inst: %s" % inst
  2291. inst.update()
  2292. #print "inst: %s" % inst.name
  2293. print "updating devices..."
  2294. if model == 'MAP':
  2295. modules = inst.get_slots()
  2296. for index in range(len(modules)):
  2297. inst = modules[index][1]
  2298. inst.slot_addr = modules[index][0]
  2299. name = 'IA3_gpib%s_%s_slot%s' % (rsrc.board, rsrc.address, inst.slot_addr)
  2300. if inst.modelNumber == 'MAPA+2370100FA':
  2301. print 'Add %s' % name
  2302. d[name] = inst
  2303. else:
  2304. namePort1 = "%s_port1" % name
  2305. print 'Add %s' % namePort1
  2306. d[namePort1] = inst
  2307. newInst = copy(inst)
  2308. newInst.device_addr = 2
  2309. name = "%s_port2" % name
  2310. print 'Add %s' % name
  2311. d[name] = newInst
  2312. else:
  2313. d[name] = inst
  2314. #print "***************"
  2315. print d[name]
  2316. #self.info("xxx", 1)
  2317. return name
  2318. # return an Instrument subclass for the model name. Return None if not found.
  2319. def get_class(model):
  2320. for clsname in __all__:
  2321. if clsname.find(model) >= 0:
  2322. return getattr(sys.modules[__name__], clsname)
  2323. return None
  2324. def get_devices():
  2325. """Return available device types in a list. """
  2326. return [ c for c in vars(sys.modules[__name__]).values() \
  2327. if type(c) is type(object) \
  2328. and issubclass(c, Instrument) \
  2329. and not c.__name__.startswith("_")]
  2330. def clear_controller(resource):
  2331. rsrc = GPIBResource()
  2332. rsrc.parse(resource)
  2333. visa.Gpib(rsrc.board).send_ifc()
  2334. def _test(argv):
  2335. addr = 'GPIB13::19'
  2336. inst = visa.instrument(addr)
  2337. cmd = '*IDN?'
  2338. val = inst.ask(cmd)
  2339. #print "IDN: %s" % val
  2340. inst.update()
  2341. if __name__ == "__main__":
  2342. _test(sys.argv)