Source code for optoanalysis.LeCroy.LeCroy

import warnings

[docs]class HDO6104: """ Class for communicating with the Teledyne LeCroy Oscilloscope. """ def __init__(self, address='152.78.194.16'): """ Initialises the connection to the Oscilloscope. Parameters ---------- address : string IP address of the oscilloscope. """ self.address = address import vxi11 self.connection = vxi11.Instrument(address) self.write = self.connection.write self.read = self.connection.read self.ask = self.connection.ask self.read_raw = self.connection.read_raw return None
[docs] def raw(self, channel=1): """ Reads the raw input from the oscilloscope. Parameters ---------- channel : int channel number of read Returns ------- rawData : bytes raw binary data read from the oscilloscope """ self.waitOPC() self.write('COMM_FORMAT DEF9,WORD,BIN') self.write('C%u:WAVEFORM?' % channel) return self.read_raw()
[docs] def data(self, channel=1): """ Reads the raw input from the scope and interprets it returning the header information, time, voltage and raw integers read with the ADC. Parameters ---------- channel : int channel number of read Returns ------- WAVEDESC : dict dictionary containing some properties of the time trace and oscilloscope settings extracted from the header file. x : ndarray The array of time values recorded by the oscilloscope y : ndarray The array of voltage values recorded by the oscilloscope integers : ndarray The array of raw integers recorded from the ADC and stored in the binary file """ raw = self.raw(channel) # Grab waveform from scope return InterpretWaveform(raw)
[docs] def waitOPC(self): """ Waits for a response from the oscilloscope indicating that processing is complete and it is ready to receive more commands. Function sleeps until the oscilloscope is ready. """ from time import sleep self.write('WAIT') while not self.opc(): sleep(1)
[docs] def opc(self): """ Asks the oscilloscope if it is done processing data. Returns ------- IsDoneProcessing : bool returns False if oscilloscope is still busy, True is oscilloscope is done processing last commands. """ return self.ask('*OPC?')[-1] == '1'
[docs]def InterpretWaveform(raw, integersOnly=False, headersOnly=False, noTimeArray=False): """ Take the raw binary from a file saved from the LeCroy, read from a file using the 2 lines: with open(filename, "rb") as file: raw = file.read() And extracts various properties of the saved time trace. Parameters ---------- raw : bytes Bytes object containing the binary contents of the saved raw/trc file integersOnly : bool, optional If True, only returns the unprocessed integers (read from the ADC) rather than the signal in volts. Defaults to False. headersOnly : bool, optional If True, only returns the file header. Defaults to False. noTimeArray : bool, optional If true returns timeStart, timeStop and timeStep and doesn't create the time array Returns ------- WAVEDESC : dict dictionary containing some properties of the time trace and oscilloscope settings extracted from the header file. x : ndarray / tuple The array of time values recorded by the oscilloscope or, if noTimeArray is True, returns a tuplef of (timeStart, timeStop, timeStep) y : ndarray The array of voltage values recorded by the oscilloscope integers : ndarray The array of raw integers recorded from the ADC and stored in the binary file MissingData : bool bool stating if any data was missing """ MissingData = False from struct import unpack if raw[0:1] != b'#': cmd = raw.split(b',')[0] # "C1:WF ALL" or similar wave = raw[len(cmd)+1:] # Remove the above command text (and trailing else: wave = raw del raw # if wave[0:1] != b'#': # warnings.warn('Waveform format not as expected, time trace may be missing data') # MissingData = True n = int(wave[1:2]) # number of digits in length of data N = int(wave[2:2+n]) # number describing length of data if wave.endswith(b'\n'): wave = wave[:-1] wave = wave[2+n:] # if N != len(wave): # warnings.warn('Length of waveform not as expected, time trace may be missing data') # MissingData = True # Code to parse WAVEDESC generated by parsing template, returned from scope query "TEMPLATE?" # Note that this is not well tested and will not handle unusual settings WAVEDESC = dict() WAVEDESC['DESCRIPTOR_NAME'] = wave[0:16].strip(b'\x00') WAVEDESC['TEMPLATE_NAME'] = wave[16:32].strip(b'\x00') WAVEDESC['COMM_TYPE'] = {0: 'byte',1: 'word'}[unpack(b"<H", wave[32:34])[0]] WAVEDESC['COMM_ORDER'] = {0: 'HIFIRST',1: 'LOFIRST'}[unpack("<H", wave[34:36])[0]] WAVEDESC['WAVE_DESCRIPTOR'] = unpack('<l', wave[36:40])[0] WAVEDESC['USER_TEXT'] = unpack('<l', wave[40:44])[0] WAVEDESC['RES_DESC1'] = unpack('<l', wave[44:48])[0] WAVEDESC['TRIGTIME_ARRAY'] = unpack('<l', wave[48:52])[0] WAVEDESC['RIS_TIME_ARRAY'] = unpack('<l', wave[52:56])[0] WAVEDESC['RES_ARRAY1'] = unpack('<l', wave[56:60])[0] WAVEDESC['WAVE_ARRAY_1'] = unpack('<l', wave[60:64])[0] WAVEDESC['WAVE_ARRAY_2'] = unpack('<l', wave[64:68])[0] WAVEDESC['RES_ARRAY2'] = unpack('<l', wave[68:72])[0] WAVEDESC['RES_ARRAY3'] = unpack('<l', wave[72:76])[0] WAVEDESC['INSTRUMENT_NAME'] = wave[76:92].strip(b'\x00') WAVEDESC['INSTRUMENT_NUMBER'] = unpack('<l', wave[92:96])[0] WAVEDESC['TRACE_LABEL'] = wave[96:112].strip(b'\x00') WAVEDESC['RESERVED1'] = unpack('<h', wave[112:114])[0] WAVEDESC['RESERVED2'] = unpack('<h', wave[114:116])[0] WAVEDESC['WAVE_ARRAY_COUNT'] = unpack('<l', wave[116:120])[0] WAVEDESC['PNTS_PER_SCREEN'] = unpack('<l', wave[120:124])[0] WAVEDESC['FIRST_VALID_PNT'] = unpack('<l', wave[124:128])[0] WAVEDESC['LAST_VALID_PNT'] = unpack('<l', wave[128:132])[0] WAVEDESC['FIRST_POINT'] = unpack('<l', wave[132:136])[0] WAVEDESC['SPARSING_FACTOR'] = unpack('<l', wave[136:140])[0] WAVEDESC['SEGMENT_INDEX'] = unpack('<l', wave[140:144])[0] WAVEDESC['SUBARRAY_COUNT'] = unpack('<l', wave[144:148])[0] WAVEDESC['SWEEPS_PER_ACQ'] = unpack('<l', wave[148:152])[0] WAVEDESC['POINTS_PER_PAIR'] = unpack('<h', wave[152:154])[0] WAVEDESC['PAIR_OFFSET'] = unpack('<h', wave[154:156])[0] WAVEDESC['VERTICAL_GAIN'] = unpack('<f', wave[156:160])[0] WAVEDESC['VERTICAL_OFFSET'] = unpack('<f', wave[160:164])[0] WAVEDESC['MAX_VALUE'] = unpack('<f', wave[164:168])[0] WAVEDESC['MIN_VALUE'] = unpack('<f', wave[168:172])[0] WAVEDESC['NOMINAL_BITS'] = unpack('<h', wave[172:174])[0] WAVEDESC['NOM_SUBARRAY_COUNT'] = unpack('<h', wave[174:176])[0] WAVEDESC['HORIZ_INTERVAL'] = unpack('<f', wave[176:180])[0] WAVEDESC['HORIZ_OFFSET'] = unpack('<d', wave[180:188])[0] WAVEDESC['PIXEL_OFFSET'] = unpack('<d', wave[188:196])[0] WAVEDESC['VERTUNIT'] = wave[196:244].strip(b'\x00') WAVEDESC['HORUNIT'] = wave[244:292].strip(b'\x00') WAVEDESC['HORIZ_UNCERTAINTY'] = unpack('<f', wave[292:296])[0] WAVEDESC['TRIGGER_TIME'] = wave[296:312] # Format time_stamp not implemented WAVEDESC['ACQ_DURATION'] = unpack('<f', wave[312:316])[0] WAVEDESC['RECORD_TYPE'] = {0: 'single_sweep',1: 'interleaved',2: 'histogram',3: 'graph',4: 'filter_coefficient',5: 'complex',6: 'extrema',7: 'sequence_obsolete',8: 'centered_RIS',9: 'peak_detect'}[unpack("<H", wave[316:318])[0]] WAVEDESC['PROCESSING_DONE'] = {0: 'no_processing',1: 'fir_filter',2: 'interpolated',3: 'sparsed',4: 'autoscaled',5: 'no_result',6: 'rolling',7: 'cumulative'}[unpack("<H", wave[318:320])[0]] WAVEDESC['RESERVED5'] = unpack('<h', wave[320:322])[0] WAVEDESC['RIS_SWEEPS'] = unpack('<h', wave[322:324])[0] WAVEDESC['TIMEBASE'] = {0: '1_ps/div',1: '2_ps/div',2: '5_ps/div',3: '10_ps/div',4: '20_ps/div',5: '50_ps/div',6: '100_ps/div',7: '200_ps/div',8: '500_ps/div',9: '1_ns/div',10: '2_ns/div',11: '5_ns/div',12: '10_ns/div',13: '20_ns/div',14: '50_ns/div',15: '100_ns/div',16: '200_ns/div',17: '500_ns/div',18: '1_us/div',19: '2_us/div',20: '5_us/div',21: '10_us/div',22: '20_us/div',23: '50_us/div',24: '100_us/div',25: '200_us/div',26: '500_us/div',27: '1_ms/div',28: '2_ms/div',29: '5_ms/div',30: '10_ms/div',31: '20_ms/div',32: '50_ms/div',33: '100_ms/div',34: '200_ms/div',35: '500_ms/div',36: '1_s/div',37: '2_s/div',38: '5_s/div',39: '10_s/div',40: '20_s/div',41: '50_s/div',42: '100_s/div',43: '200_s/div',44: '500_s/div',45: '1_ks/div',46: '2_ks/div',47: '5_ks/div',100: 'EXTERNAL'}[unpack("<H", wave[324:326])[0]] WAVEDESC['VERT_COUPLING'] = {0: 'DC_50_Ohms',1: 'ground',2: 'DC_1MOhm',3: 'ground',4: 'AC_1MOhm'}[unpack("<H", wave[326:328])[0]] WAVEDESC['PROBE_ATT'] = unpack('<f', wave[328:332])[0] WAVEDESC['FIXED_VERT_GAIN'] = {0: '1_uV/div',1: '2_uV/div',2: '5_uV/div',3: '10_uV/div',4: '20_uV/div',5: '50_uV/div',6: '100_uV/div',7: '200_uV/div',8: '500_uV/div',9: '1_mV/div',10: '2_mV/div',11: '5_mV/div',12: '10_mV/div',13: '20_mV/div',14: '50_mV/div',15: '100_mV/div',16: '200_mV/div',17: '500_mV/div',18: '1_V/div',19: '2_V/div',20: '5_V/div',21: '10_V/div',22: '20_V/div',23: '50_V/div',24: '100_V/div',25: '200_V/div',26: '500_V/div',27: '1_kV/div'}[unpack("<H", wave[332:334])[0]] WAVEDESC['BANDWIDTH_LIMIT'] = {0: 'off',1: 'on'}[unpack("<H", wave[334:336])[0]] WAVEDESC['VERTICAL_VERNIER'] = unpack('<f', wave[336:340])[0] WAVEDESC['ACQ_VERT_OFFSET'] = unpack('<f', wave[340:344])[0] WAVEDESC['WAVE_SOURCE'] = {0: 'CHANNEL_1',1: 'CHANNEL_2',2: 'CHANNEL_3',3: 'CHANNEL_4',9: 'UNKNOWN'}[unpack("<H", wave[344:346])[0]] if len(wave[346:]) != WAVEDESC['WAVE_ARRAY_1']: warnings.warn('Binary data not the expected length, time trace may be missing data') MissingData = True if headersOnly: return WAVEDESC, MissingData else: from numpy import fromstring, int16, arange if MissingData != True: integers = fromstring(wave[346:], dtype=int16) else: integers = fromstring(wave[346:][:-1], dtype=int16) if integersOnly: return (WAVEDESC, integers, MissingData) elif noTimeArray: y = integers * WAVEDESC['VERTICAL_GAIN'] - WAVEDESC['VERTICAL_OFFSET'] x = arange(len(integers)) * WAVEDESC['HORIZ_INTERVAL'] + WAVEDESC['HORIZ_OFFSET'] timeStart = x[0] timeStop = x[-1] timeStep = x[1]-x[0] return (WAVEDESC, (timeStart, timeStop, timeStep), y, integers, MissingData) else: y = integers * WAVEDESC['VERTICAL_GAIN'] - WAVEDESC['VERTICAL_OFFSET'] x = arange(len(integers)) * WAVEDESC['HORIZ_INTERVAL'] + WAVEDESC['HORIZ_OFFSET'] return (WAVEDESC, x, y, integers, MissingData)