# jds6600.py # library to remote-control a JDS6600 signal generator # Kristoff Bonne (c) 2018 # published under MIT license. See file "LICENSE" for full license text # Revisions: # Version 0.0.1: 2018/01/19: initial release, reading basic parameters # version 0.0.2: 2018/01/28: added "measure" menu + support functions, documentation # version 0.0.3: 2018/02/07: added "counter" and "sweep" menu # version 0.0.4: 2018/02/14: added "pulse" and "burst" menu + code cleanup # version 0.0.5: 2018/02/16: added system menu # version 0.1.0: 2018/02/17: added arbitrary waveform import serial import binascii ########### # Errors # ########### class UnknownChannelError(ValueError): pass class UnexpectedValueError(ValueError): pass class UnexpectedReplyError(ValueError): pass class FormatError(ValueError): pass class WrongMode(RuntimeError): # called when commands are issued with the jds6600 not in the correct # mode pass ################# # jds6600 class # ################# class jds6600: 'jds6600 top-level class' # serial device (opened during object init)) ser = None # commands DEVICETYPE=0 SERIALNUMBER=1 CHANNELENABLE=20 WAVEFORM1=21 WAVEFORM2=22 FREQUENCY1=23 FREQUENCY2=24 AMPLITUDE1=25 AMPLITUDE2=26 OFFSET1=27 OFFSET2=28 DUTYCYCLE1=29 DUTYCYCLE2=30 PHASE=31 ACTION=32 MODE=33 MEASURE_COUP=36 # coupling (AC or DC) MEASURE_GATE=37 # gatetime MEASURE_MODE=38 # mode (Freq or Periode) COUNTER_COUPL=MEASURE_COUP COUNTER_RESETCOUNTER=39 SWEEP_STARTFREQ=40 SWEEP_ENDFREQ=41 SWEEP_TIME=42 SWEEP_DIRECTION=43 SWEEP_MODE=44 # mode: linair or Log PULSE_PULSEWIDTH=45 PULSE_PERIOD=46 PULSE_OFFSET=47 PULSE_AMPLITUDE=48 BURST_NUMBER=49 BURST_MODE=50 SYSTEM_SOUND=51 SYSTEM_BRIGHTNESS=52 SYSTEM_LANGUAGE=53 SYSTEM_SYNC=54 SYSTEM_ARBMAXNUM=55 PROFILE_SAVE=70 PROFILE_LOAD=71 PROFILE_CLEAR=72 COUNTER_DATA_COUNTER=80 MEASURE_DATA_FREQ_LOWRES=81 # low resolution freq counter, used for mode "frequency" MEASURE_DATA_FREQ_HIGHRES=82 # high resolution freq. counter, usef for mode "period". UI: valid up to 2 Khz MEASURE_DATA_PW1=83 MEASURE_DATA_PW0=84 MEASURE_DATA_PERIOD=85 MEASURE_DATA_DUTYCYCLE=86 MEASURE_DATA_U1=87 MEASURE_DATA_U2=88 MEASURE_DATA_U3=89 # waveforms: registers 21 (ch1) and 22 (ch2)) # 0 to 16: predefined waveforms __wave=("SINE","SQUARE","PULSE","TRIANGLE","PARTIALSINE","CMOS","DC","HALF-WAVE","FULL-WAVE","POS-LADDER","NEG-LADDER", "NOISE", "EXP-RIZE","EXP-DECAY","MULTI-TONE","SINC","LORENZ") # 101 to 160: arbitrary waveforms __awave=[] for a in range(1,10): __awave.append("ARBITRARY0"+str(a)) for a in range(10,61): __awave.append("ARBITRARY"+str(a)) # end for # modes: register 33 # note: use lowest 4 bits for wrting # note: use highest 4 bits for reading __modes = ((0,"WAVE_CH1"),(0,"WAVE_CH1"),(1,"WAVE_CH2"),(1,"WAVE_CH2"),(2,"SYSTEM"),(2,"SYSTEM"),(-1,""),(-1,""),(4,"MEASURE"),(5,"COUNTER"),(6,"SWEEP_CH1"),(7,"SWEEP_CH2"),(8,"PULSE"),(9,"BURST")) # action: register 32 __actionlist=(("STOP","0,0,0,0"),("COUNT","1,0,0,0"),("SWEEP","0,1,0,0"),("PULSE","1,0,1,1"),("BURST","1,0,0,1")) __action={} for (actionname,actioncode) in __actionlist: __action[actionname]=actioncode # end for # measure mode parameters __measure_coupling=("AC(EXT.IN)","DC(EXT.IN)") __measure_mode=("M.FREQ","M.PERIOD") # sweep parameters __sweep_direction=("RISE","FALL","RISE&FALL") __sweep_mode=("LINEAR","LOGARITHM") # burst parameters __burst_mode=["MANUAL TRIG.","CH2 TRIG.","EXT.TRIG(AC)","EXT.TRIG(DC)"] # frequency multiplier __freqmultiply=(1,1,1,1/1000,1/1000000) # language __system_language=("ENGLISH","CHINESE") ############### # oonstructor # ############### def __init__(self,fname): jds6600.ser = serial.Serial( port= fname, baudrate=115200, parity=serial.PARITY_NONE, stopbits=serial.STOPBITS_ONE, bytesize=serial.EIGHTBITS, timeout=1 ) # end constructor ##################### # support functions # ##################### ##### # low-level support function # parse data from read command def __parsedata(self,reg,data,a): if a not in (0,1): raise RuntimeError(a) try: (one,two)=data.split("=") except ValueError: raise FormatError("Parsing Returned data: Invalid format, missing \"=\"") two_b=two.split(".") # reads from register are terminated by a "." # reads of arbitrary waveform are not if a == 0: if len(two_b) < 2: raise FormatError("Parsing Returned data: Invalid format, missing \".\"") if len(two_b) > 2: raise FormatError("Parsing Returned data: Invalid format, too many \".\"") # end if # command to look for; # "r" for register read, "b" for arbitrary waveform read c = 'r' if a == 0 else 'b' # check if returned data matches reg that was send if reg != None: if one != ":"+c+reg: errmsg="Parsing Return data: send/received reg mismatch: "+data+" / expected :"+c+reg raise FormatError(errmsg) # end if # end if # done: return data: part between "=" and ".", split on "," return two_b[0].split(",") # end __parsedata # command in textual form def __reg2txt(self,reg): return "0"+str(reg) if int(reg) < 10 else str(reg) # end reg2txt # send read command (for n datapoint) def __sendreadcmd(self,reg,n,a): if type(n) != int: raise TypeError(n) if a not in (0,1): raise ValueError(a) regtxt=self.__reg2txt(reg) if (n < 1): raise ValueError(n) if a == 0: # a (arbitrary waveform) is 0 -> register read c='r' # register else: c='b' # arbitrary waveform # for n to 1 n=1 # end else - if # "n" in command start with 0 for 1 read-request n -= 1 if self.ser.is_open == True: tosend=":"+c+regtxt+"="+str(n)+"."+chr(0x0a) self.ser.write(tosend.encode()) # end __sendreadcmd # get responds of read-request and parse ("n" reads) def __getrespondsandparse(self,reg, n, a): if type(n) != int: raise ValueError(n) if a not in (0,1): raise ValueError(a) # a=0-> register read, a=1 -> arbitrary waveform read ret=[] # return value c = int(reg) # counter c_expect=self.__reg2txt(c) for l in range(n): # get one line responds from serial device retserial=self.ser.readline() # convert bytearray into string, then strip off terminating \n and \r retserial=str(retserial,'utf-8').rstrip() # get parsed data # assume all data are single-value fields parseddata=self.__parsedata(c_expect,retserial,a) # we receive a list of strings, all containing numeric (integer) values if len(parseddata) == 1: # if list with one value, return that value (converted to integer) ret.append(int(parseddata[0])) else: # if list with multiple values, convert all strings to integers and return list retlist=[] retcount=0 for data in parseddata: if data == "": # we should not receive empty datafields, except for after the last element of an arbitrary waveform if not ((a == 1) and (retcount == 2048)): raise UnexpectedValueError(parseddata) else: retlist.append(int(data)) # end else - if retcount += 1 # end for ret.append(retlist) # end else - if # increase next expected to-receive data c += 1 c_expect=self.__reg2txt(c) # end for # return parsed data # if only one element, return that element # if multiple elements, return list return ret[0] if n == 1 else ret # end __get responds and parse 1 # get data def __getdata(self,reg, n=1, a=0): if type(reg) != int: raise TypeError(reg) if type(n) != int: raise TypeError(n) # a is "arbitrary waveform or register" # a=0 -> register read # a=1 -> arbitrary waveform read # send "read" commandline for "n" lines # copy "a" parameter from calling function self.__sendreadcmd(reg,n,a) return self.__getrespondsandparse(reg,n,a) # end __getdata 1 # send write command and wait for "ok" def __sendwritecmd(self,reg, val, a=0): # note: a = "arbitrary waveform?": 0 = no (register write), 1 = yes (arb. waveform write) # add a "0" to the command if it one single character reg=self.__reg2txt(reg) # command to send: "w" for register write, "b" for arbitrary waveform write cmd = "w" if a == 0 else "a" if self.ser.is_open == True: if type(val) == int: val = str(val) if type(val) != str: raise TypeError(val) tosend=":"+cmd+reg+"="+val+"."+chr(0x0a) self.ser.write(tosend.encode()) # wait for "ok" # get one line responds from serial device ret=self.ser.readline() # convert bytearray into string, then strip off terminating \n and \r ret=str(ret,'utf-8').rstrip() if ret != ":ok": raise UnexpectedReplyError(ret) # end if #end if # end __sendwritecmd ##### # high-level support function # set action def __setaction(self,action): # type check if type(action) != str: raise TypeError(action) # end if try: self.__sendwritecmd(jds6600.ACTION,jds6600.__action[action]) except KeyError: errmsg="Unknown Action: "+action raise ValueError(errmsg) # end try # end set action ################### # DEBUG functions # ################### def DEBUG_readregister(self,register,count): if self.ser.is_open == True: regtxt=self.__reg2txt(register) tosend=":r"+regtxt+"="+str(count)+"."+chr(0x0a) self.ser.write(tosend.encode()) ret=self.ser.readline() while ret != b'': print(str(ret)) ret=self.ser.readline() # end while # end if # end readregister def DEBUG_writeregister(self,register,value): if self.ser.is_open == True: regtxt=self.__reg2txt(register) if type(value) == int: value=str(value) tosend=":w"+regtxt+"="+value+"."+chr(0x0a) self.ser.write(tosend.encode()) ret=self.ser.readline() while ret != b'': print(str(ret)) ret=self.ser.readline() # end while # end if # end write register ############## # PUBLIC API # ############## ######################### # Part 0: API information # API version def getAPIinfo_version(self): return 1 # end getAPIversion # API release number def getAPIinfo_release(self): return "0.1.0 2018-02-17" # end get API release ############################# # Part 1: information queries # list of waveforms def getinfo_waveformlist(self): waveformlist=list(enumerate(jds6600.__wave)) for aw in (enumerate(jds6600.__awave,101)): waveformlist.append(aw) # end for return waveformlist # end get waveform list # get list of modes def getinfo_modelist(self): modelist=[] lastmode=-1 # create list of modes, removing dups for (modeid,modetxt) in jds6600.__modes: # ignore modes with modeid < 0 (unused mode) if modeid < 0: continue # end if if modeid != lastmode: modelist.append((modeid,modetxt)) lastmode = modeid # end if # end for return modelist # end getinfo modelist ################################## # Part 2: reading basic parameters # get device type def getinfo_devicetype(self): return self.__getdata(jds6600.DEVICETYPE) # end get device type # get serial number def getinfo_serialnumber(self): return self.__getdata(jds6600.SERIALNUMBER) # end get serial number # get channel enable status def getchannelenable(self): (ch1,ch2)=self.__getdata(jds6600.CHANNELENABLE) try: return (False,True)[ch1], (False,True)[ch2] except IndexError: errmsg="Unexpected value received: {},{}".format(ch1,ch2) raise UnexpectedValueError(errmsg) # end get channel enable status # get waveform def getwaveform(self, channel): if type(channel) != int: raise TypeError(channel) if not (channel in (1,2)): raise ValueError(channel) #WAVEFORM for channel 2 is WAVEFORM1 + 1 waveform=self.__getdata(jds6600.WAVEFORM1+channel-1) # waveform 0 to 16 are in "wave" list, 101 to 160 are in __awave try: return (waveform,jds6600.__wave[waveform]) except IndexError: pass try: return (waveform,jds6600.__awave[waveform-101]) except IndexError: raise UnexpectedValueError(waveform) # end getwaveform # get frequency _with multiplier def getfrequency_m(self,channel): if type(channel) != int: raise TypeError(channel) if not (channel in (1,2)): raise ValueError(channel) (f1,f2)=self.__getdata(jds6600.FREQUENCY1+channel-1) # parse multiplier (value after ",") # 0=Hz, 1=KHz,2=MHz, 3=mHz,4=uHz) # note f1 is frequency / 100 try: return((f1/100*self.__freqmultiply[f2],f2)) except IndexError: # unexptected value of frequency multiplier raise UnexpectedValueError(f2) # end elsif # end function getfreq # get frequency _no multiplier information def getfrequency(self,channel): if type(channel) != int: raise TypeError(channel) if not (channel in (1,2)): raise ValueError(channel) (f1,f2)=self.__getdata(jds6600.FREQUENCY1+channel-1) # parse multiplier (value after ","): 0=Hz, 1=KHz,2=MHz, 3=mHz,4=uHz) # note1: frequency unit is Hz / 100 # note2: multiplier 1 (khz) and 2 (mhz) only changes the visualisation on the # display of the jfs6600. The frequency itself is calculated in # the same way as for multiplier 0 (Hz) # mulitpliers 3 (mHZ) and 4 (uHz) do change the calculation of the frequency try: return(f1/100*self.__freqmultiply[f2]) except IndexError: # unexptected value of frequency multiplier raise UnexpectedValueError(f2) # end elsif # end function getfreq # get amplitude def getamplitude(self, channel): if type(channel) != int: raise TypeError(channel) if not (channel in (1,2)): raise ValueError(channel) amplitude=self.__getdata(jds6600.AMPLITUDE1+channel-1) # amplitude is mV -> so divide by 1000 return amplitude/1000 # end getamplitude # get offset def getoffset(self, channel): if type(channel) != int: raise TypeError(channel) if not (channel in (1,2)): raise ValueError(channel) offset=self.__getdata(jds6600.OFFSET1+channel-1) # offset unit is 10 mV, and then add 1000 return (offset-1000)/100 # end getoffset # get dutcycle def getdutycycle(self, channel): if type(channel) != int: raise TypeError(channel) if not (channel in (1,2)): raise ValueError(channel) dutycycle=self.__getdata(jds6600.DUTYCYCLE1+channel-1) # dutycycle unit is 0.1 %, so divide by 10 return dutycycle/10 # end getdutycycle # get phase def getphase(self): phase=self.__getdata(jds6600.PHASE) # phase unit is 0.1 degrees, so divide by 10 return phase/10 # end getphase ################################## # Part 3: writing basic parameters # set channel enable def setchannelenable(self,ch1,ch2): if type(ch1) != bool: raise TypeError(ch1) if type(ch2) != bool: raise TypeError(ch1) # channel 1 if ch1 == True: enable = "1" else: enable = "0" # end else - if if ch2 == True: enable += ",1" else: enable += ",0" # end else - if # write command self.__sendwritecmd(jds6600.CHANNELENABLE,enable) # end set channel enable # set waveform def setwaveform(self,channel,waveform): if type(channel) != int: raise TypeError(channel) if (type(waveform) != int) and (type(waveform) != str): raise TypeError(waveform) if not (channel in (1,2)): raise ValueError(channel) # wzveform can be integer or string w=None if type(waveform) == int: # waveform is an integer if waveform < 101: try: jds6600.__wave[waveform] except IndexError: raise ValueError(waveform) # end try else: try: jds6600.__awave[waveform-101] except IndexError: raise ValueError(waveform) # end try # end if # ok, it exists! w=waveform else: # waveform is a string # make everything uppercase waveform=waveform.upper() # check all waveform descriptions in wave and __awave # w is already initialised as "none" above try: # try in "wave" list w=jds6600.__wave.index(waveform) except ValueError: pass if w == None: #if not found in "wave", try the "awave" list try: w=jds6600.__awave.index(waveform)+101 # arbitrary waveforms state are index 101 except ValueError: pass # end try # end if if w == None: # not in "wave" and "awave" error errmsg="Unknown waveform "+waveform raise ValueError (errmsg) # end if # ens else - if self.__sendwritecmd(jds6600.WAVEFORM1+channel-1,w) # end function set waveform # set frequency (with multiplier) def setfrequency(self,channel,freq,multiplier=0): if type(channel) != int: raise TypeError(channel) if (type(freq) != int) and (type(freq) != float): raise TypeError(freq) if type(multiplier) != int: raise TypeError(multiplier) if not (channel in (1,2)): raise ValueError(channel) if (freq < 0): raise ValueError(freq) # do not execute set-frequency when the device is in sweepfrequency mode currentmode=self.getmode() if (channel == 1) and (currentmode[1] == "SWEEP_CH1"): # for channel 1 raise WrongMode() elif (channel == 2) and (currentmode[1] == "SWEEP_CH2"): # for channel 2 raise WrongMode() # end elsif - if # freqmultier should be one of the "frequency multiply" values try: self.__freqmultiply[multiplier] except IndexError: raise ValueError[multiplier] # frequency limit: # 60 Mhz for multiplier 0 (Hz), 1 (KHz) and 2 (MHz) # 80 Khz for multiplier 3 (mHz) # 80 Hz for multiplier 4 (uHz) # trying to configure a higher value can result in incorrect frequencies if multiplier < 3: if freq > 60000000: errmsg="Maximum frequency using multiplier {} is 60 MHz.".format(multiplier) raise ValueError(errmsg) # end if elif multiplier == 3: if freq > 80000: errmsg="Maximum frequency using multiplier 3 is 80 KHz." raise ValueError(errmsg) # end if else: # multiplier == 4 if freq > 80: errmsg="Maximum frequency using multiplier 4 is 80 Hz." raise ValueError(errmsg) # end if # end else - elsif - if # round to nearest 0.01 value freq=int(round(freq*100/jds6600.__freqmultiply[multiplier])) value=str(freq)+","+str(multiplier) self.__sendwritecmd(jds6600.FREQUENCY1+channel-1,value) # end set frequency (with multiplier) # set amplitude def setamplitude(self, channel, amplitude): if type(channel) != int: raise TypeError(channel) if (type(amplitude) != int) and (type(amplitude) != float): raise TypeError(amplitude) if not (channel in (1,2)): raise ValueError(channel) # amplitude is between 0 and 20 V if not (0 <= amplitude <= 20): raise ValueError(amplitude) # round to nearest 0.001 value amplitude=int(round(amplitude*1000)) self.__sendwritecmd(jds6600.AMPLITUDE1+channel-1,amplitude) # end setamplitude # set offset def setoffset(self, channel, offset): if type(channel) != int: raise TypeError(channel) if (type(offset) != int) and (type(offset) != float): raise TypeError(offset) if not (channel in (1,2)): raise ValueError(channel) # offset is between -10 and +10 volt if not (-10 <= offset <= 10): raise ValueError(offset) # note: althou the value-range for offset is able # to accomodate an offset between -10 and +10 Volt # the actual offset seams to be lmited to -2.5 to +2.5 # round to nearest 0.01 value offset=int(round(offset*100))+1000 self.__sendwritecmd(jds6600.OFFSET1+channel-1, offset) # end set offset # set dutcycle def setdutycycle(self, channel, dutycycle): if type(channel) != int: raise TypeError(channel) if (type(dutycycle) != int) and (type(dutycycle) != float): raise TypeError(dutycycle) if not (channel in (1,2)): raise ValueError(channel) # dutycycle is between 0 and 100 % if not (0 <= dutycycle <= 100): raise ValueError(dutycycle) # round to nearest 0.1 value dutycycle=int(round(dutycycle*10)) self.__sendwritecmd(jds6600.DUTYCYCLE1+channel-1,dutycycle) # end set dutycycle # set phase def setphase(self,phase): if (type(phase) != int) and (type(phase) != float): raise TypeError(phase) # hase is between -360 and 360 if not (-360 <= phase <= 360): raise ValueError(phase) if phase < 0: phase += 3600 # round to nearest 0.1 value phase=int(round(phase*10)) self.__sendwritecmd(jds6600.PHASE,phase) # end getphase ####################### # Part 4: reading / changing mode # get mode def getmode(self): mode=self.__getdata(jds6600.MODE) # mode is in the list "modes". mode-name "" means undefinded mode=int(mode)>>3 try: (modeid,modetxt)=jds6600.__modes[mode] except IndexError: raise UnexpectedValueError(mode) # modeid 3 is not valid and returns an id of -1 if modeid >= 0: return modeid,modetxt # end if # modeid 4 raise UnexpectedValueError(mode) # end getmode # set mode def setmode(self,mode, nostop=False): if (type(mode) != int) and (type(mode) != str): raise TypeError(mode) modeid=-1 # if mode is an integer, it should be between 0 and 9 if type(mode) == int: if not (0 <= mode <= 9): raise ValueError(mode) # end if # modeid 3 / modetxt "" does not exist if mode == 3: raise ValueError("mode 3 does not exist") # end if # valid modeid modeid = mode else: # modeid 3 / modetxt "" does not exist if mode == "": raise ValueError("mode 3 does not exist") # end if # mode is string -> check list # (note: the modes-list is not enumerated like the other lists, so an "array.index("text")" search is not possible for mid,mtxt in jds6600.__modes: if mode.upper() == mtxt: # found it!!! modeid=mid break # end if else: # mode not found -> error raise ValueError(mode) # end for # end else - if # before changing mode, disable all actions (unless explicitally asked not to do) if nostop == False: self.__setaction("STOP") # endif # set mode self.__sendwritecmd(jds6600.MODE,modeid) # if new mode is "burst", reset burst counter if modeid == 9: self.burst_resetcounter() # end if # end setmode ####################### # Part 5: functions common for all modes def stopallactions(self): # just send stop self.__setaction("STOP") # end stop all actions ####################### # Part 6: "measure" mode # get coupling parameter (measure mode) def measure_getcoupling(self): coupling=self.__getdata(jds6600.MEASURE_COUP) try: return (coupling,jds6600.__measure_coupling[coupling]) except IndexError: raise UnexpectedValueError(coupling) # end try # end get coupling (measure mode) # get gate time (measure mode) # get (measure mode) def measure_getgate(self): gate=self.__getdata(jds6600.MEASURE_GATE) # gate unit is 0.01 seconds return gate / 100 # end get gate (measure mode) # get Measure mode (freq or period) def measure_getmode(self): mode=self.__getdata(jds6600.MEASURE_MODE) try: return (mode,jds6600.__measure_mode[mode]) except IndexError: raise UnexpectedValueError(mode) # end try # end get mode (measure) # set measure coupling def measure_setcoupling(self,coupling): # type checks if (type(coupling) != int) and (type(coupling) != str): raise TypeError(coupling) if type(coupling) == int: # coupling is 0 (DC) or 1 (AC) if not (coupling in (0,1)): raise ValueError(coupling) coupl=coupling else: # string based coupling=coupling.upper() # spme shortcuts: if coupling == "AC": coupling = "AC(EXT.IN)" if coupling == "DC": coupling = "DC(EXT.IN)" try: coupl=jds6600.__measure_coupling.index(coupling) except ValueError: errmsg="Unknown measure-mode coupling: "+coupling raise ValueError(errmsg) # end try # end else ) if (type is int or str?) # set mode self.__sendwritecmd(jds6600.MEASURE_COUP,coupl) # end set measure_coupling # set gate time (measure mode) def measure_setgate(self, gate): # check type if (type(gate) != int) and (type(gate) != float): raise TypeError(gate) # gate unit is 0.01 and is between 0 and 10 if not (0 < gate <= 1000): raise ValueError(gate) gate = int(round(gate*100)) # minimum is 0.01 second if gate == 0: gate = 0.01 # set gate self.__sendwritecmd(jds6600.MEASURE_GATE,gate) # end get gate (measure mode) # set measure mode def measure_setmode(self,mode): # type checks if (type(mode) != int) and (type(mode) != str): raise TypeError(mode) if type(mode) == int: # mode is 0 (M.FREQ) or 1 (M.PRERIOD) if not (mode in (0,1)): raise ValueError(mode) else: # string based # make uppercase mode=mode.upper() # spme shortcuts: if mode== "FREQ": mode = "M.FREQ" if mode== "PERIOD": mode = "M.PERIOD" try: mode=jds6600.__measure_mode.index(mode) except ValueError: errmsg="Unknown measure mode: "+mode raise ValueError(errmsg) # end try # end else ) if (type is int or str?) # set mode self.__sendwritecmd(jds6600.MEASURE_MODE,mode) # end set measure_coupling # get Measure freq. (lowres / Freq-mode) def measure_getfreq_f(self): freq=self.__getdata(jds6600.MEASURE_DATA_FREQ_LOWRES) # unit is 0.1 Hz return freq/10 # end get freq-Lowres (measure) # get Measure freq. (highes / Periode-mode) def measure_getfreq_p(self): freq=self.__getdata(jds6600.MEASURE_DATA_FREQ_HIGHRES) # unit is 0.001 Hz return freq/1000 # end get freq-Lowres (measure) # get Measure pulsewith + def measure_getpw1(self): pw=self.__getdata(jds6600.MEASURE_DATA_PW1) # unit is 0.01 microsecond return pw/100 # end get pulsewidth - # get Measure pulsewidth - def measure_getpw0(self): pw=self.__getdata(jds6600.MEASURE_DATA_PW0) # unit is 0.01 microsecond return pw/100 # end get pulsewidth + # get Measure total period def measure_getperiod(self): period=self.__getdata(jds6600.MEASURE_DATA_PERIOD) # unit is 0.01 microsecond return period/100 # end get total period # get Measure dutycycle def measure_getdutycycle(self): dutycycle=self.__getdata(jds6600.MEASURE_DATA_DUTYCYCLE) # unit is 0.1 % return dutycycle/10 # end get freq-Lowres (measure) # get Measure unknown value 1 (related to freq, inverse-related to gatetime) def measure_getu1(self): # unit is unknown, just return it return self.__getdata(jds6600.MEASURE_DATA_U1) # end get freq-Lowres (measure) # get Measure unknown value 2 (inverse related to freq) def measure_getu2(self): # unit is unknown, just return it return self.__getdata(jds6600.MEASURE_DATA_U2) # end get freq-Lowres (measure) # get Measure unknown value 3 (nverse related to freq) def measure_getu3(self): # unit is unknown, just return it return self.__getdata(jds6600.MEASURE_DATA_U3) # end get freq-Lowres (measure) # get Measure all (all usefull data: freq_f, freq_p, pw1, pw0, period, dutycycle def measure_getall(self): # do query for 6 values (start with freq_q) (freq_f, freq_p, pw1, pw0, period, dutycycle)=self.__getdata(jds6600.MEASURE_DATA_FREQ_LOWRES,6) # return all return (freq_f / 10, freq_p / 1000, pw1/100, pw0/100, period/100, dutycycle/10) # end get freq-Lowres (measure) ####################### # Part 7: "Counter" mode # counter getcoupling is the same as measure getcoupling def counter_getcoupling(self): return self.measure_getcoupling() # end counter get coupling # get counter - counter def counter_getcounter(self): # unit is 1, just return data return self.__getdata(jds6600.COUNTER_DATA_COUNTER) # end get counter - counter # counter setcoupling is the same as measure setcoupling def counter_setcoupling(self,coupling): self.measure_setcoupling(coupling) # end counter get coupling # counter - reset counter def counter_reset(self): # write 0 to register "COUNTER_RESETCOUNTER" self.__sendwritecmd(jds6600.COUNTER_RESETCOUNTER,0) # end counter reset counter # start counter mode def counter_start(self): mode=self.getmode() if mode[1] != "COUNTER": raise WrongMode() # end if # action start BURST mode self.__setaction("COUNT") # end counter_start def counter_stop(self): # just send stop self.__setaction("STOP") # end counter_stop ####################### # Part 8: "Sweep" mode # note, there are two "setmode" commands to enter "sweep" mode: "sweep_ch1' (mode 6) and "sweep_ch2" (mode 7) def sweep_getstartfreq(self): # unit is 0.01Hz -> divide by 100 return self.__getdata(jds6600.SWEEP_STARTFREQ)/100 # end get sweep - startfreq def sweep_getendfreq(self): # unit is 0.01 Hz -> divide by 100 return self.__getdata(jds6600.SWEEP_ENDFREQ)/100 # end get sweep - startfreq def sweep_gettime(self): # unit is 0.1 sec -> divide by 10 return self.__getdata(jds6600.SWEEP_TIME)/10 # end get sweep - startfreq # get sweep direction def sweep_getdirection(self): direction=self.__getdata(jds6600.SWEEP_DIRECTION) try: return (direction,jds6600.__sweep_direction[direction]) except IndexError: raise UnexpectedValueError(direction) # end try # end get direction (sweep) # get sweep mode def sweep_getmode(self): mode=self.__getdata(jds6600.SWEEP_MODE) try: return (mode,jds6600.__sweep_mode[mode]) except IndexError: raise UnexpectedValueError(mode) # end try # end get mode (measure) def sweep_setstartfreq(self, frequency): if (type(frequency) != int) and (type(frequency) != float): raise TypeError(frequency) # frequency should be between 0 and 60 MHz if not (0 <= frequency <= 60000000): raise ValueError(frequency) # frequency unit is 0.01 Hz freq=int(round(frequency*100)) self.__sendwritecmd(jds6600.SWEEP_STARTFREQ,freq) # end set start freq def sweep_setendfreq(self, frequency): if (type(frequency) != int) and (type(frequency) != float): raise TypeError(frequency) # frequency should be between 0 and 60 MHz if not (0 <= frequency <= 60000000): raise ValueError(frequency) # frequency unit is 0.01 Hz freq=int(round(frequency*100)) self.__sendwritecmd(jds6600.SWEEP_ENDFREQ,freq) # end set end freq def sweep_settime(self, time): if (type(time) != int) and (type(time) != float): raise TypeError(time) # time should be between 0 and 999.9 seconds if not (0 < time <= 999.9): raise ValueError(time) # time unit is 0.1 second t=int(round(time*10)) self.__sendwritecmd(jds6600.SWEEP_TIME,t) # end set end freq def sweep_setdirection(self, direction): if (type(direction) != int) and (type(direction) != str): raise TypeError(direction) if type(direction) == int: # mode is 0 (RISE), 1 (FALL) or 2 (RISE&FALL) if not (direction in (0,1,2)): raise ValueError(direction) else: # string based # make uppercase direction=direction.upper() # spme shortcuts: if direction == "RISEFALL": direction = "RISE&FALL" if direction == "BOTH": direction = "RISE&FALL" try: direction=jds6600.__sweep_direction.index(direction) except ValueError: errmsg="Unknown sweep direction: "+direction raise ValueError(errmsg) # end else - for # end else ) if (type is int or str?) # set mode self.__sendwritecmd(jds6600.SWEEP_DIRECTION,direction) # end set direction (sweep) def sweep_setmode(self, mode): if (type(mode) != int) and (type(mode) != str): raise TypeError(mode) if type(mode) == int: # mode is 0 (LINEAR) or 1 (LOGARITHM)) if not (mode in (0,1)): raise ValueError(mode) else: # string based # make uppercase mode=mode.upper() # spme shortcuts: if mode == "LIN": mode = "LINEAR" if mode == "LOG": mode = "LOGARITHM" try: mode=jds6600.__sweep_mode.index(mode) except ValueError: errmsg="Unknown sweep mode: "+mode raise ValueError(errmsg) # end else - for # end else ) if (type is int or str?) # set mode self.__sendwritecmd(jds6600.SWEEP_MODE,mode) # end set direction (sweep) # get sweep channel def sweep_getchannel(self): mode=self.getmode() if mode[1] == "SWEEP_CH1": return 1 elif mode[1] == "SWEEP_CH2": return 2 # end if - elif # not channel 1 or channel 2, return 0 return 0 # end sweep get_channel # set sweep channel def sweep_setchannel(self,channel): if type(channel) != int: raise TypeError(channel) # new channel should be 1 or 2 if not (channel in (1,2)): raise ValueError(channel) # end if # get current channel (1 or 2, or 0 if not in sweep mode) currentchannel=self.sweep_getchannel() # only swich if already in sweep mode if currentchannel == 0: raise WrongMode() #endif # switch if the new channel is different from current channel if channel != currentchannel: if channel == 1: self.setmode("SWEEP_CH1",nostop=True) else: self.setmode("SWEEP_CH2",nostop=True) # end else - if # end if # end sweep set channel # start sweep def sweep_start(self): mode=self.getmode() if (mode[1] != "SWEEP_CH1") and (mode[1] != "SWEEP_CH2"): raise WrongMode() # end if # action start BURST mode self.__setaction("SWEEP") # end sweep_start # stop sweep def sweep_stop(self): # just send stop self.__setaction("STOP") # end sweep_start ################################## ####################### # Part 9: PULSE # get pulsewidth, normalised to s def pulse_getpulsewidth(self): # pulsewith returns two datafiels, periode + multiplier time,multi = self.__getdata(jds6600.PULSE_PULSEWIDTH) if multi==0: return time / 1000000000 # ns elif multi == 1: return time / 1000000 # us else: UnexptectedValue(multi) # end else - elsif - if # end # get pulsewidth, not normalised def pulse_getpulsewidth_m(self): # pulsewith returns two datafiels, periode + multiplier return self.__getdata(jds6600.PULSE_PULSEWIDTH) # end # get period, normalised to s def pulse_getperiod(self): # period returns two datafiels, periode + multiplier time,multi = self.__getdata(jds6600.PULSE_PERIOD) if multi==0: return time / 1000000000 # ns elif multi == 1: return time / 1000000 # us else: UnexptectedValue(multi) # end else - elsif - if # end # get period, not normalised def pulse_getperiod_m(self): # period returns two datafiels, periode + multiplier return self.__getdata(jds6600.PULSE_PERIOD) # end # get offset def pulse_getoffset(self): # unit is %, just return data return self.__getdata(jds6600.PULSE_OFFSET) # end # get amplitude def pulse_getamplitude(self): # unit 0.01, divide by 100 return self.__getdata(jds6600.PULSE_AMPLITUDE)/100 # end # set pulsewith or period (backend function) def __pulse_setpw_period(self, var, data, multiplier, normalised): maxval=([4e9,4e9],(4,4000)) # maximum data value minval=((30,1),(30e-9,1e-6)) # minimum data value multi=(1e9,1e6) # unit is 1ns or 1 us data2reg=(jds6600.PULSE_PULSEWIDTH,jds6600.PULSE_PERIOD) data2txt=("pulsewidth","period") if (type(var) != int) or ((var != 0) and (var != 1)): raise RuntimeError("error __pulse_setpw_period: var") # var is 0 (for pulsewidth) or 1 (period) if (type(data) != int) and (type(data) != float): raise TypeError(period) if (type(normalised) != int) or not (normalised in (0,1)): raise RuntimeError("error __pulse_setpw_period: normalised") if type(multiplier) != int: raise TypeError(multiplier) # multiplier should be 0 or 1 if not (multiplier in (0,1)): errmsg="multiplier must be 0 (ns) or 1 (us)" raise ValueError(errmsg) # end if # data must be between to allocated limits: # not normalised: allowed values: 30 to 400000000 (multi=0) / 1 to 4000000000 (multi=1) # normalised: allowed values: 30 ns to 4 sec. (multi=0) / 1 us to 4000 sec (multi=2) if not (minval[normalised][multiplier] <= data <= maxval[normalised][multiplier]): errmsg="{} must be between {} and {}".format(data2txt[var],minval[normalised][multiplier],maxval[normalised][multiplier]) raise ValueError(errmsg) # end if # convert from s to ns/us, if needed if normalised == 1: data = str(round(data * multi[multiplier]))+","+str(multiplier) else: data = str(int(data))+","+str(multiplier) # end if # done: now write self.__sendwritecmd(data2reg[var],data) # end set pw/period, low-level function # set pw, normalised def pulse_setpulsewidth(self,pw,multiplier=0): # convert to low-level function self.__pulse_setpw_period(0,pw,multiplier,1) # end pulse_setpw (normalised) # set pw, not normalised def pulse_setpulsewidth_m(self,pw,multiplier): # convert to low-level function self.__pulse_setpw_period(0,pw,multiplier,0) # end pulse_setpw (normalised) # set period, normalised def pulse_setperiod(self,pw,multiplier=0): # convert to low-level function self.__pulse_setpw_period(1,pw,multiplier,1) # end pulse_setperiod (normalised) # set period, not normalised def pulse_setperiod_m(self,pw,multiplier): # convert to low-level function self.__pulse_setpw_period(1,pw,multiplier,0) # end pulse_setperiod (normalised) # set pulse offset def pulse_setoffset(self,offset): if (type(offset) != int) and (type(offset) != float): raise TypeError(offset) # offset is between 0 and 120 % if not (0 <= offset <= 120): errmsg="Offset must be between 0 and 120 %" raise ValueError(errmsg) # end if # unit = 1 -> just send self.__sendwritecmd(jds6600.PULSE_OFFSET,offset) # end off # set pulse amplitude def pulse_setamplitude(self,amplitude): if (type(amplitude) != int) and (type(amplitude) != float): raise TypeError(amplitude) # amplitude is between 0 and 10 V if not (0 <= amplitude <= 10): errmsg="Amplitude must be between 0 and 10 Volt" raise ValueError(errmsg) # end if # unit is 0.01V amplitude=int(round(amplitude*100)) self.__sendwritecmd(jds6600.PULSE_AMPLITUDE,amplitude) # end off def pulse_start(self): mode=self.getmode() if mode[1] != "PULSE": raise WrongMode() # end if # action start BURST mode self.__setaction("PULSE") # end pulse_start def pulse_stop(self): # just send stop self.__setaction("STOP") # end pulse_stop ####################### # Part 10: BURST def burst_getnumberofbursts(self): # unit is 1, just return value return self.__getdata(jds6600.BURST_NUMBER) # end burst get number of bursts def burst_getmode(self): mode = self.__getdata(jds6600.BURST_MODE) try: return(mode,jds6600.__burst_mode[mode]) except IndexError: raise UnexpectedValue(mode) # end try # end burst get mode def burst_setnumberofbursts(self,burst): if type(burst) != int: raise TypeError(burst) # number of burst should be between 1 and 1048575 if not (1 <= burst <= 1048575): errmsg="Number of bursts should be between 1 and 1048575" raise ValueError(errmsg) # end if # unit is 1, just return value self.__sendwritecmd(jds6600.BURST_NUMBER,burst) # end burst get number of bursts def burst_setmode(self,mode): if (type(mode) != int) and (type(mode) != str): raise TypeError(mode) if type(mode) == int: # mode input is an integer # mode should be between 0 and 3 if not (0 <= mode <= 3): errmsg="Burst mode should between 0 and 3" raise ValueError(mode) # end if else: # mode input is a string mode=mode.upper() # shortcuts if mode == "MANUAL": mode = "MANUAL TRIG." if mode == "CH2": mode = "CH2 TRIG." if mode == "EXT.AC": mode = "EXT.TRIG(AC)" if mode == "EXT.DC": mode = "EXT.TRIG(DC)" try: mode=jds6600.__burst_mode.index(mode.upper()) except ValueError: errmsg="Unknown burst mode: "+mode raise ValueError(errmsg) # end try # end else - if # stop if the burst-mode is running self.burst_stop() # write command self.__sendwritecmd(jds6600.BURST_MODE,mode) def burst_resetcounter(self): # reset counter: same as counter_reset_counter self.counter_reset() # end reset counter def burst_start(self): mode=self.getmode() if mode[1] != "BURST": raise WrongMode() # end if # action start BURST mode self.__setaction("BURST") # end burst_start def burst_stop(self): # just send stop self.__setaction("STOP") # end burst_start ####################### # Part 11: SYSTEM ######## # part 11.1: system parameters # NOTE: there is a strange behaviour in some jds6600 devices where that the register to # read the sysem-parameters is one higher then the registernumber use to write # the parameter-value. # This is probably a bug. The bug-fix code to deal with this situation can be # be overwriten adding a "bugfix=0" option in the system_get* API-calls # get sound setting def system_getsound(self, bugfix=True): if bugfix == True: sound=self.__getdata(jds6600.SYSTEM_SOUND+1) elif bugfix == False: sound=self.__getdata(jds6600.SYSTEM_SOUND) else: TypeError(bugfix) # we should receive a 0 or 1 try: return (False,True)[sound] except IndexError: raise UnexpectedValueError(sound) #end system_getsound # get brightness setting def system_getbrightness(self, bugfix=True): if bugfix == True: return self.__getdata(jds6600.SYSTEM_BRIGHTNESS+1) elif bugfix == False: return self.__getdata(jds6600.SYSTEM_BRIGHTNESS) else: TypeError(bugfix) #end system_getbrightness # get language setting def system_getlanguage(self, bugfix=True): if bugfix == True: language=self.__getdata(jds6600.SYSTEM_LANGUAGE+1) elif bugfix == False: language=self.__getdata(jds6600.SYSTEM_LANGUAGE) else: TypeError(bugfix) # we should receive a 0 or a 1 try: return language,jds6600.__system_language[language] except KeyError: raise UnexpectedValueError(sound) #end system_getlanguage def system_getsync(self, bugfix=True): if bugfix == True: sync=self.__getdata(jds6600.SYSTEM_SYNC+1) elif bugfix == False: sync=self.__getdata(jds6600.SYSTEM_SYNC) else: TypeError(bugfix) # returns a list of 5 fields: frequency, wave, amplitude, dutycycle and offset if len(sync) != 5: raise UnexpectedValueError(sync) # end if # return data ret=[] for s in sync: if s not in (0,1): raise UnexpectedValueError(sync) ret.append((False,True)[s]) # end for # return data return ret # end system_getsync # get maximum number of arbitrary waveforms def system_getarbmaxnum(self, bugfix=True): if bugfix == True: return self.__getdata(jds6600.SYSTEM_ARBMAXNUM+1) elif bugfix == False: return self.__getdata(jds6600.SYSTEM_ARBMAXNUM) else: TypeError(bugfix) #end system_getlanguage # set system sound def system_setsound(self,sound): if type(sound) != bool: raise TypeError(sound) if sound == True: self.__sendwritecmd(jds6600.SYSTEM_SOUND,1) else: self.__sendwritecmd(jds6600.SYSTEM_SOUND,0) # end set sound # set system brightness def system_setbrightness(self,brightness): if type(brightness) != int: raise TypeError(brightness) # should be between 1 and 12 if not(1 <= brightness <= 12): raise ValueError(brightness) self.__sendwritecmd(jds6600.SYSTEM_BRIGHTNESS,brightness) # end set sound # set system language def system_setlanguage(self,language): if (type(language) != int) and (type(language) != str): raise TypeError(language) if type(language) == int: # integer if language not in (0,1): raise ValueError(sound) else: # string # shortcuts: if language == "EN": language = "ENGLISH" if language == "CH": language = "CHINESE" try: language=jds6600.__system_language.index(language.upper()) except ValueError: errmsg="Unknown language: "+language raise ValueError(errmsg) # end try # end else - if self.__sendwritecmd(jds6600.SYSTEM_LANGUAGE,language) # reinit "mode" to refresh screen for language change to become active (mode,modetxt)=self.getmode() self.setmode(mode) # end set language # set system sync def system_setsync(self,freq,wave,ampl,duty,offs): if type(freq) != bool: raise TypeError(freq) if type(wave) != bool: raise TypeError(wave) if type(ampl) != bool: raise TypeError(ampl) if type(duty) != bool: raise TypeError(duty) if type(offs) != bool: raise TypeError(offs) # create command to send sync=[freq,wave,ampl,duty,offs] for i in range(5): sync[i] = '1' if sync[i] == True else '0' # merge all 5 elements in one command, seperated by "," self.__sendwritecmd(jds6600.SYSTEM_SYNC,",".join(sync)) # end set sync # set maximum number of arbitrary waveforms def system_setarbmaxnum(self,arbmaxnum): if type(arbmaxnum) != int: raise TypeError(arbmaxnum) # abrmaxnum should be between 1 and 60 if not(1 <= arbmaxnum <= 60): raise ValueError(arbmaxnum) self.__sendwritecmd(jds6600.SYSTEM_ARBMAXNUM,arbmaxnum) # end set arbmaxnum ###### # part 11.2: save / read / clear profile def system_saveprofile(self,profile): if type(profile) != int: raise TypeError(profile) # profile is between 0 and 99 if not(0 <= profile <= 99): raise ValueError(errmsg) # write profile to "PROFILE_SAVE" self.__sendwritecmd(jds6600.PROFILE_SAVE,profile) # end profile save def system_loadprofile(self,profile): if type(profile) != int: raise TypeError(profile) # profile is between 0 and 99 if not(0 <= profile <= 99): raise ValueError(errmsg) # write profile to "PROFILE_LOAD" self.__sendwritecmd(jds6600.PROFILE_LOAD,profile) # end profile load def system_clearprofile(self,profile): if type(profile) != int: raise TypeError(profile) # profile is between 0 and 99 if not(0 <= profile <= 99): raise ValueError(errmsg) # write profile to "PROFILE_CLEAR" self.__sendwritecmd(jds6600.PROFILE_CLEAR,profile) # end profile clear ####################### # Part 12: Arbitrary waveform operations def arb_getwave(self,waveid): if type(waveid) != int: raise TypeError(waveid) # waveid is between 1 and 60 if not(1 <= waveid <= 60): raise ValueError(waveid) # getdata, reg=waveform id, data = 1, a=1 (register/waveform selector) return self.__getdata(waveid,1,a=1) # end get arbtrary waveform def arb_setwave(self,waveid,wave): if type(waveid) != int: raise TypeError(waveid) if (type(wave) != tuple) and (type(wave) != list): raise TypeError(wave) # waveid is between 1 and 60 if not(1 <= waveid <= 60): raise ValueError(waveid) # wave should be a list or tuple of 2048 elements, all integers, with a value between 0 and 4095 if len(wave) != 2048: raise ValueError(wave) tosend="" for val in wave: if type(val) != int: raise ValueError(wave) if not (0 <= val <= 4095): raise ValueError(wave) tosend += (str(val) if tosend=="" else ","+str(val)) # end for # write waveform, reg=waveform id, data = waveform, a=1 (register/waveform selector) self.__sendwritecmd(waveid,tosend,a=1) # end set arbirtary waveform ################################## # end class jds6600