# 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