Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
176 changes: 176 additions & 0 deletions Python/Thorlabs WM20X Wavelength Meters/PyTLWAVE.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
import os
import ctypes

class TLWAVE:

def __init__(self, resourceName = None, IDQuery = True, resetDevice = False):
"""
This function initializes the instrument driver session and performs the following initialization actions:

(1) Opens a session to the Default Resource Manager resource and a session to the specified device using the Resource Name.
(2) Performs an identification query on the instrument.
(3) Resets the instrument to a known state.
(4) Sends initialization commands to the instrument.
(5) Returns an instrument handle which is used to distinguish between different sessions of this instrument driver.

Notes:
(1) Each time this function is invoked a unique session is opened.

Args:
resourceName
IDQuery (bool):This parameter specifies whether an identification query is performed during the initialization process.

VI_TRUE (1): Do query (default).
VI_FALSE (0): Skip query.


resetDevice (bool):This parameter specifies whether the instrument is reset during the initialization process.

VI_TRUE (1): instrument is reset
VI_FALSE (0): no reset (default)


"""
possiblepaths = [os.path.dirname(os.path.abspath(__file__)),
r"C:\Program Files (x86)\Thorlabs\OPM"]

if ctypes.sizeof(ctypes.c_voidp) == 4:
dll_name = "TLWAVE_32.dll"
else:
dll_name = "TLWAVE_64.dll"

self.dll = None
last_err = None
for path in possiblepaths:
fullpath = os.path.join(path,dll_name)
if os.path.isfile(fullpath):
try:
self.dll = ctypes.cdll.LoadLibrary(fullpath)
break
except OSError as e:
print(e)
last_err = e

if self.dll is None:
print(last_err)
raise RuntimeError(f"Could not find {dll_name} in {" or ".join(possiblepaths)}")

self.devSession = ctypes.c_long()
self.devSession.value = 0
if resourceName is not None:
pInvokeResult = self.dll.TLWAVE_init( ctypes.create_string_buffer(resourceName), ctypes.c_bool(IDQuery), ctypes.c_bool(resetDevice), ctypes.byref(self.devSession))
self.__testForError(pInvokeResult)


def __testForError(self, status):
if status < 0:
self.__throwError(status)
return status

def __throwError(self, code):
msg = ctypes.create_string_buffer(1024)
self.dll.TLWAVE_error_message(self.devSession, ctypes.c_int(code), msg)
raise NameError(ctypes.c_char_p(msg.raw).value)

def getRsrcName(self, index):
"""
This function gets the resource name string needed to open a device.

Notes:
(1) The data provided by this function was updated at the last call of <Find Resources>.

Args:
index(uint32) : This parameter accepts the index of the device to get the resource descriptor from.

Notes:
(2) The index is zero based. The maximum index to be used here is one less than the number of devices found by the last call of <Find Resources>.

Returns:
resourceName(string) : This parameter returns the resource descriptor. Use this descriptor to specify the device.
"""
pyresourceName = ctypes.create_string_buffer(1024)
pInvokeResult = self.dll.TLWAVE_getRsrcName(self.devSession, ctypes.c_uint32(index), pyresourceName)
self.__testForError(pInvokeResult)
return ctypes.c_char_p(pyresourceName.raw).value

def findRsrc(self):
"""
This function finds all driver compatible devices attached to the PC and returns the number of found devices.

Note:
(1) The function additionally stores information like system name about the found resources internally. This information can be retrieved with further functions from the class, e.g. <Get Resource Description> and <Get Resource Information>.


Args:

Returns:
resourceCount(uint32) : The number of connected devices that are supported by this driver.
"""
pyresourceCount = ctypes.c_uint32(0)
pInvokeResult = self.dll.TLWAVE_findRsrc(self.devSession, ctypes.byref(pyresourceCount))
self.__testForError(pInvokeResult)
return pyresourceCount.value

def __del__(self):
#destructor
if self.dll is not None:
self.close()

def close(self):
"""
This function closes the instrument driver session.

Note: The instrument must be reinitialized to use it again.

Returns:
int: The return value, 0 is for success
"""
pInvokeResult = self.dll.TLWAVE_close(self.devSession)
return pInvokeResult

def writeRaw(self, command):
"""
This function writes directly to the instrument.

Args:
command(char_p) : Null terminated command string to send to the instrument.

Returns:
"""
pInvokeResult = self.dll.TLWAVE_writeRaw(self.devSession, ctypes.c_char_p(command.encode('utf-8')))
self.__testForError(pInvokeResult)

def readRaw(self, size):
"""
This function reads directly from the instrument.

Args:

size(uint32) : This parameter specifies the buffer size.

Notes:
(1) If received data is less than buffer size, the buffer is additionally terminated with a '' character.
(2) If received data is same as buffer size no '' character is appended. It's the caller's responsibility to make sure a buffer is '' terminated if the caller wants to interpret the buffer as string.
(3) You may pass VI_NULL if you don't need this value.

Returns:
buffer(string) : Byte buffer that receives the data read from the instrument.
returnCount(uint32) : Number of bytes actually transferred and filled into Buffer. This number doesn't count the additional termination '' character. If Return Count == size of the buffer, the content will not be '' terminated.
"""
pybuffer = ctypes.create_string_buffer(1024)
pyreturnCount = ctypes.c_uint32(0)
pInvokeResult = self.dll.TLWAVE_readRaw(self.devSession, pybuffer, ctypes.c_uint32(size), ctypes.byref(pyreturnCount))
self.__testForError(pInvokeResult)
return ctypes.c_char_p(pybuffer.raw).value, pyreturnCount.value

def readRegister(self, registerID:int) -> int:
registervalue = ctypes.c_int16(-1)
pInvokeResult = self.dll.TLWAVE_readRegister(self.devSession, registerID, ctypes.byref(registervalue))
self.__testForError(pInvokeResult)
return registervalue.value

def getID(self) -> str:
self.writeRaw("*IDN?\n")
rawValue,numbytes = self.readRaw(1024)
id = rawValue.decode('utf-8').strip()
return id
23 changes: 23 additions & 0 deletions Python/Thorlabs WM20X Wavelength Meters/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
## Included Example

### Thorlabs WM20X Wavelength Meters
In this folder you will find sample codes showing how to control and acquire from
Thorlabs WM20X Wavelength Meters.

There are 4 examples in this folder:

* [Example 1 - Read the wavelength using TCP](./Thorlabs_WM_example_01.py)
* [Example 2 - Read the wavelength using TLWAVE dll (USB/TCP)](./Thorlabs_WM_example_02.py)
* [Example 3 - Read the wavelength using RS232](./Thorlabs_WM_example_03.py)
* [Example 4 - Set fast mode, then read wavelength and estimated power](./Thorlabs_WM_example_04.py)


### Requirements
* The example with TCP-connection is cross-plattform compatible, and uses only native python.

* The example with USB connection requires windows and installation of [Optical power meter software](https://www.thorlabs.com/software_pages/ViewSoftwarePage.cfm?Code=OPM), which includes the USB-driver as well as the needed TLWAVE dll. The DLL is wrapepd by PyTLWAVE.py which is found in the same folder as the example.

* The example with RS232-connection is cross-plattform compatible, but utilizes pyserial.

### More information
For details about the available SCPI commands please refer to the instrument manual.
64 changes: 64 additions & 0 deletions Python/Thorlabs WM20X Wavelength Meters/Thorlabs_WM_example_01.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
"""
Thorlabs_VM_example_01
Example Date of Creation: 2025-08-22
Example Date of Last Modification on Github: 2025-08-22
Version of Python: 3.12.4
Version of Platform Firmware: 0.9.12
Version of Interferometer Firmware: 0.58
==================
Example Description: Read the wavelength from Thorlabs Wavelength Meter WM20X using TCP
"""
import time
import socket
import struct

# Set the address to the IP number of your wavelength meter
wavemeteradress = "192.168.55.6"

def build_request_frame(cmd):
# This creates the TCP frames that are sent to the wavelength meter.
# The basic example only supports single frame requests
# Full example of the TCP frames can be found with the PM5020 examples
payload = cmd.encode()
FRAME_START = 0xCA
return struct.pack('<BBH', FRAME_START, 0, len(payload)) + payload

def recv_response(sock):
# Recieves and parses a TCP packet from the wavelength meter.
data = b''
seq = 0
HEADER_SIZE = 4
while True:
# Loop because response might be multi frame
hdr = sock.recv(HEADER_SIZE)
if len(hdr) < HEADER_SIZE:
break
start, cnt, plen = struct.unpack('<BBH', hdr)
if cnt != seq:
break
part = sock.recv(plen)
data += part
if start != 0xCB:
break # no more frames
seq += 1
return data.decode(errors='ignore')

def send_cmd(sock, cmd):
# Build and send a SCPI command to the wavelength meter
sock.sendall(build_request_frame(cmd))

def request_response(sock, cmd):
# Send a SCPI command to the wavelength meter and
# read the answer from it
send_cmd(sock, cmd)
return recv_response(sock)

with socket.create_connection((wavemeteradress, 2000), timeout=5) as s:
while True:
try:
response = request_response(s, "MEAS:WAV?\n")
wavelength = float(response.strip())
print(f"{wavelength} nm(vac)")
except Exception as e:
print(e)
time.sleep(1)
76 changes: 76 additions & 0 deletions Python/Thorlabs WM20X Wavelength Meters/Thorlabs_WM_example_02.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
"""
Thorlabs_VM_example_02
Example Date of Creation: 2025-08-22
Example Date of Last Modification on Github: 2025-08-22
Version of Python: 3.13.7
Version of the Thorlabs SDK used: OPM 6.5, TLWAVE_32.dll
Version of Platform Firmware: 0.9.12
Version of Interferometer Firmware: 0.58
==================
Example Description: Read the wavelength from Thorlabs WM20X Wavelength Meter using TLWAVE dll (USB/TCP)
"""
import time
from PyTLWAVE import TLWAVE

discover = True

wavemeter = None

if discover:
# Here we try to find wavemeters using USB
wavemetersession = TLWAVE()

deviceCount = wavemetersession.findRsrc()
print("devices found: " + str(deviceCount))

resourceNames = []
for i in range(deviceCount):
localName = wavemetersession.getRsrcName(i)
print(localName)
resourceNames.append(localName)

wavemetersession.close()

wavemeter = TLWAVE(resourceNames[0])
else:
# Here is an example of connecting to using ethernet to a specific ip
wavemeter = TLWAVE(b"TCPIP0::192.168.55.6::2000::SOCKET", False)

def waitfornewdata():
"""Wait for new data flag (9th bit) in status register"""
newdata = False
newstatus = False
while not newdata:
wavemeter.writeRaw("STAT:OPER:COND?\n")
value, numbytes = wavemeter.readRaw(1024)
try:
reg_value = int(value)
if reg_value is not None:
if (reg_value & 512) == 512: # Checking the 9th bit
newdata = True
if (reg_value & 1024) == 1024: # Checking the 10th bit
newstatus = True
except Exception as e:
print(f"Error parsing response: {e}, got: '{value}'")

if not newdata:
time.sleep(0.1)
if newstatus:
wavemeter.writeRaw("SENS:STAT:COND?\n")
rawValue, numbytes = wavemeter.readRaw(1024)
statuscode = int(rawValue)
print(f"New status: {statuscode}")
newstatus = False



for i in range(1000):
# Loop and print the wavelength continuously
wavemeter.writeRaw("FETC:WAV?\n")
rawValue, numbytes = wavemeter.readRaw(1024)
wavelength = float(rawValue)
print(f"{wavelength} nm(vac)")
waitfornewdata()

wavemeter.close()

30 changes: 30 additions & 0 deletions Python/Thorlabs WM20X Wavelength Meters/Thorlabs_WM_example_03.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
"""
Thorlabs_VM_example_03
Example Date of Creation: 2025-08-22
Example Date of Last Modification on Github: 2025-08-22
Version of Python: 3.12.4
Version of Platform Firmware: 0.9.12
Version of Interferometer Firmware: 0.58
==================
Example Description: Read the wavelength from Thorlabs WM20X Wavelength Meter using RS232
"""
import time
import serial

ser = serial.Serial("COM7", 115200, timeout=2) # Modify COM7 to your serial port

def request_response(cmd):
# This writes a command to the serial port,
# and then waits for the answer and returns it.
ser.write(cmd.encode())
response = ser.readline().decode('utf-8')
return f"{response}"

while True:
try:
response = request_response("MEAS:WAV?\n")
wavelength = float(response.strip())
print(f"{wavelength} nm(vac)")
except Exception as e:
print(e)
time.sleep(1)
Loading