From 3f63276da71250dd8bdcb3c72235d1813bc96296 Mon Sep 17 00:00:00 2001
From: Dustin Brunner <dbrun21@outlook.de>
Date: Sat, 27 Aug 2022 11:45:59 +0200
Subject: [PATCH] V1

---
 README.md                           |   62 +
 helper_scripts/auto_detect_port.py  |   17 +
 helper_scripts/list_serial_ports.py |   16 +
 influxdb_config.py                  |   10 +
 jds6600.py                          | 1893 +++++++++++++++++++++++++++
 readfreq_influxdb.py                |  101 ++
 readfreq_terminal.py                |   54 +
 7 files changed, 2153 insertions(+)
 create mode 100644 README.md
 create mode 100644 helper_scripts/auto_detect_port.py
 create mode 100644 helper_scripts/list_serial_ports.py
 create mode 100644 influxdb_config.py
 create mode 100644 jds6600.py
 create mode 100644 readfreq_influxdb.py
 create mode 100644 readfreq_terminal.py

diff --git a/README.md b/README.md
new file mode 100644
index 0000000..0dc7398
--- /dev/null
+++ b/README.md
@@ -0,0 +1,62 @@
+# Influx-DB frequency logger with JDS6600 signal generator
+
+A simple python script to write measured frequency values into a Influx-DB database.
+
+## Python installation on Ubuntu
+```
+sudo apt-get update
+sudo apt-get install python3
+sudo apt-get install python3-pip
+```
+
+## Python Dependencies:
+```
+pip install pyserial
+pip install influxdb
+```
+
+## running the script (automatically)
+The script is auto-detecting the USB-port, where the signal generator is connected to. After that it connects to a Influx-DB-server and then publishes the measured frequency value every second to this server. Make sure to fill your login credentials into the `influxdb_config.py`-file.
+
+To auto start the script at the sotartup of the server we configure a systemd-service. I  assume the repository with this script is cloned into the home directory of one user.
+
+```
+sudo nano /etc/systemd/system/readfreq_influxdb.service
+```
+Write into this file:
+```
+[Unit]
+Description=readfreq_influxdb python script
+After=multi-user.target
+
+[Service]
+Type=simple
+Restart=always
+RestartSec=5
+ExecStart=/usr/bin/python3 /home/user/software_python/readfreq_influxdb.py
+StandardOutput=syslog
+StandardError=syslog
+
+[Install]
+WantedBy=multi-user.target
+```
+
+No the autostart can be activated:
+```
+sudo systemctl enable readfreq_influxdb.service
+sudo systemctl start readfreq_influxdb.service
+sudo systemctl status readfreq_influxdb.service
+```
+To deactivate it, use the command `sudo systemctl disable readfreq_influxdb.service`.
+
+
+# Sources/useful links:
+- Python Lib (MIT License): https://github.com/on1arf/jds6600_python
+- https://github.com/thomaseichhorn/funcgen
+- https://www.thomaschristlieb.de/ein-python-script-mit-systemd-als-daemon-systemd-tut-garnicht-weh/
+
+
+<br><br>
+<p xmlns:dct="http://purl.org/dc/terms/" xmlns:cc="http://creativecommons.org/ns#" class="license-text">This work by <span property="cc:attributionName">Dustin Brunner</span> is licensed under <a rel="license" href="https://creativecommons.org/licenses/by/4.0">CC BY 4.0<img style="height:15px!important;margin-left:3px;vertical-align:text-bottom;" src="https://mirrors.creativecommons.org/presskit/icons/cc.svg?ref=chooser-v1" /><img style="height:15px!important;margin-left:3px;vertical-align:text-bottom;" src="https://mirrors.creativecommons.org/presskit/icons/by.svg?ref=chooser-v1" /></a></p>
+
+<a rel="license" href="http://creativecommons.org/licenses/by/4.0/"><img alt="Creative Commons Lizenzvertrag" style="border-width:0" src="https://i.creativecommons.org/l/by/4.0/88x31.png" /></a><br />Dieses Werk von <span xmlns:cc="http://creativecommons.org/ns#" property="cc:attributionName">Dustin Brunner</span> ist lizenziert unter einer <a rel="license" href="http://creativecommons.org/licenses/by/4.0/">Creative Commons Namensnennung 4.0 International Lizenz</a>.
diff --git a/helper_scripts/auto_detect_port.py b/helper_scripts/auto_detect_port.py
new file mode 100644
index 0000000..38dba1c
--- /dev/null
+++ b/helper_scripts/auto_detect_port.py
@@ -0,0 +1,17 @@
+import warnings
+import serial
+import serial.tools.list_ports
+
+found_ports = [
+    p.device
+    for p in serial.tools.list_ports.comports()
+    if 'CH340' in p.description or 'USB Serial' in p.description    #CH340 for Windows, USB Serial for Linux
+]
+
+if not found_ports:
+    raise IOError("No JDS6600-device found")
+if len(found_ports) > 1:
+    warnings.warn('Multiple JDS6600-devices found - using the first')
+
+portname = found_ports[0]
+print(portname)
\ No newline at end of file
diff --git a/helper_scripts/list_serial_ports.py b/helper_scripts/list_serial_ports.py
new file mode 100644
index 0000000..7e38b25
--- /dev/null
+++ b/helper_scripts/list_serial_ports.py
@@ -0,0 +1,16 @@
+import os
+import serial.tools.list_ports
+
+ports = serial.tools.list_ports.comports(include_links=True)
+
+print("Serial Ports:")
+print("path \t\t| name \t\t| description")
+print("-------------------------------------------------------------------------")
+
+for port in ports :
+    print(port.device + " \t\t| " + port.name + "\t\t| " + port.description)
+
+print("-------------------------------------------------------------------------")
+
+os.system("pause")
+    
\ No newline at end of file
diff --git a/influxdb_config.py b/influxdb_config.py
new file mode 100644
index 0000000..809b3f4
--- /dev/null
+++ b/influxdb_config.py
@@ -0,0 +1,10 @@
+# ----------------
+# Config file
+# ----------------
+
+# Config for the Influxdb-server
+influxdb_host = '192.168.1.10'
+influxdb_port = 8086
+influxdb_user = 'user'
+influxdb_password = '123456'
+influxdb_dbname = 'frequency'
diff --git a/jds6600.py b/jds6600.py
new file mode 100644
index 0000000..feb179e
--- /dev/null
+++ b/jds6600.py
@@ -0,0 +1,1893 @@
+# 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
diff --git a/readfreq_influxdb.py b/readfreq_influxdb.py
new file mode 100644
index 0000000..48715e3
--- /dev/null
+++ b/readfreq_influxdb.py
@@ -0,0 +1,101 @@
+from jds6600 import jds6600
+import warnings
+import serial
+import serial.tools.list_ports
+import time
+from influxdb import InfluxDBClient
+from datetime import datetime
+import influxdb_config
+
+print("-----------------JDS6600-Reader-----------------")
+print("Searching Device ...")
+
+found_ports = [
+    p.device
+    for p in serial.tools.list_ports.comports()
+    if 'CH340' in p.description or 'USB Serial' in p.description    #CH340 for Windows, USB Serial for Linux
+]
+if not found_ports:
+    raise IOError("No JDS6600-device found")
+if len(found_ports) > 1:
+    warnings.warn('Multiple JDS6600-devices found - using the first')
+
+portname = found_ports[0]
+print("JDS6600 device found!")
+print("Using Port ", portname)
+
+jds = jds6600(portname)
+#jds = jds6600("COM4")
+
+print("--------------------------")
+# API information calls
+print("Devicetype: ", jds.getinfo_devicetype())
+print("Serialnumber:", jds.getinfo_serialnumber())
+print("--------------------------")
+
+#Disable Both outputs
+#print(jds.getchannelenable())
+jds.setchannelenable(bool(0), bool(0))
+print("Disabeling Outputs ... \t\t OK")
+
+#print(jds.getmode())
+jds.setmode('MEASURE')
+print("Set Mode Measure ... \t\t OK")
+
+jds.measure_setcoupling('AC')
+jds.measure_setgate(1) #Gatetime 1s
+jds.measure_setmode("PERIOD")
+print("Configure Measurement ... OK")
+print("--------------------------")
+
+
+print("Connecting to Influx-DB ... OK")
+dbclient = InfluxDBClient(  influxdb_config.influxdb_host,
+                            influxdb_config.influxdb_port, 
+                            influxdb_config.influxdb_user, 
+                            influxdb_config.influxdb_password, 
+                            influxdb_config.influxdb_dbname)
+freq_value = 0.0
+dbclient_success = False
+dbclient_fail_counter = 0
+
+print("--------------------------")
+print("Starting to read data ...")
+print("--------------------------")
+
+log_count = 0
+
+while 1:
+    if jds.getmode()[0] != 4:   # 4 means mode 'MEASURE'
+        raise IOError("Measurement-mode is not enabled!")
+    freq_value = jds.measure_getfreq_p()
+
+    
+
+    json_body = [
+        {
+            "measurement": "freq_Hz",
+            "fields": {
+                "value": freq_value
+            }
+        }
+    ]
+
+    dbclient_success = dbclient.write_points(json_body, time_precision='ms')
+     
+    if dbclient_success == True:
+        log_count = log_count + 1
+        if log_count >= 10:
+            print("f = ", freq_value, " DB OK")
+            log_count = 0
+    else:
+        dbclient_fail_counter = dbclient_fail_counter + 1
+        print("f = ", freq_value, " DB ERROR ", dbclient_fail_counter)
+        if dbclient_fail_counter >= 5:
+            raise IOError("Writing DB entry failed 5 times ... exiting!")
+        else:
+            dbclient_fail_counter = 0
+    
+
+    time.sleep(1)
+
diff --git a/readfreq_terminal.py b/readfreq_terminal.py
new file mode 100644
index 0000000..70a58ce
--- /dev/null
+++ b/readfreq_terminal.py
@@ -0,0 +1,54 @@
+
+from jds6600 import jds6600
+import warnings
+import serial
+import serial.tools.list_ports
+import time
+
+print("-----------------JDS6600-Reader-----------------")
+print("Searching Device ...")
+
+found_ports = [
+    p.device
+    for p in serial.tools.list_ports.comports()
+    if 'CH340' in p.description or 'USB Serial' in p.description    #CH340 for Windows, USB Serial for Linux
+]
+if not found_ports:
+    raise IOError("No JDS6600-device found")
+if len(found_ports) > 1:
+    warnings.warn('Multiple JDS6600-devices found - using the first')
+
+portname = found_ports[0]
+print("JDS6600 device found!")
+print("Using Port ", portname)
+
+jds = jds6600(portname)
+#jds = jds6600("COM4")
+
+print("--------------------------")
+# API information calls
+print("Devicetype: ", jds.getinfo_devicetype())
+print("Serialnumber:", jds.getinfo_serialnumber())
+print("--------------------------")
+
+#Disable Both outputs
+#print(jds.getchannelenable())
+jds.setchannelenable(bool(0), bool(0))
+print("Disabeling Outputs ... \t\t OK")
+
+#print(jds.getmode())
+jds.setmode('MEASURE')
+print("Set Mode Measure ... \t\t OK")
+
+jds.measure_setcoupling('AC')
+jds.measure_setgate(1) #Gatetime 1s
+jds.measure_setmode("PERIOD")
+print("Configure Measurement ... OK")
+print("--------------------------")
+
+while 1:
+    if jds.getmode()[0] != 4:   # 4 means mode 'MEASURE'
+        raise IOError("Measurement-mode is not enabled!")
+    print(jds.measure_getfreq_p())
+    time.sleep(1)
+