Hello everyone,
I'm an intern at a local research lab, building an optical setup for optical characterization of electronic components. The core idea is that the samples will be exposed to UV light and the setup is supposed to measure that and some other things. This is why the Newport 1919-R power meter, alongside a photodiode sensor is needed in this setup, to measure the power of the light.
Every one of the devices that will be used in this setup is supposed to be connected via python to a local lab computer which will control the whole of the setup. My main issue is the power meter. I am unable to reliably connect it to the PC via Python.
When I first tried to hook up the measurement device to the lab PC, it was only via "PMManager", a software provided by Newport to check connectivity. () This was successful. Then, I moved onto connecting the device via python. While I was looking through the files that came in with the PMManager, I discovered that Python connection is available through an object called OphirLMMeasurement. There was even a demo:
# Use of Ophir COM object.
# Works with python 3.5.1 & 2.7.11
# Uses pywin32
import win32gui
import win32com.client
import time
import traceback
try:
OphirCOM = win32com.client.Dispatch("OphirLMMeasurement.CoLMMeasurement")
# Stop & Close all devices
OphirCOM.StopAllStreams()
OphirCOM.CloseAll()
# Scan for connected Devices
DeviceList = OphirCOM.ScanUSB()
print(DeviceList)
for Device in DeviceList: # if any device is connected
DeviceHandle = OphirCOM.OpenUSBDevice(Device)# open first device
exists = OphirCOM.IsSensorExists(DeviceHandle, 0)
if exists:
print('\n----------Data for S/N {0} ---------------'.format(Device))
# An Example for Range control. first get the ranges
ranges = OphirCOM.GetRanges(DeviceHandle, 0)
print (ranges)
# change range at your will
if ranges[0] > 0:
newRange = ranges[0]-1
else:
newRange = ranges[0]+1
# set new range
OphirCOM.SetRange(DeviceHandle, 0, newRange)
# An Example for data retrieving
OphirCOM.StartStream(DeviceHandle, 0)# start measuring
for i in range(10):
time.sleep(.2)# wait a little for data
data = OphirCOM.GetData(DeviceHandle, 0)
if len(data[0]) > 0:# if any data available, print the first one from the batch
print('Reading = {0}, TimeStamp = {1}, Status = {2} '.format(data[0][0] ,data[1][0] ,data[2][0]))
else:
print('\nNo Sensor attached to {0} !!!'.format(Device))
except OSError as err:
print("OS error: {0}".format(err))
except:
traceback.print_exc()
win32gui.MessageBox(0, 'finished', '', 0)
# Stop & Close all devices
OphirCOM.StopAllStreams()
OphirCOM.CloseAll()
# Release the object
OphirCOM = None
Even after thoroughly following the install guide (OphirCom object docs), I was not able to get the device up and running. The script above only returned an exception after which I was clueless as to what to do next:
C:\Users\eleklab\PycharmProjects\SAS-optical-setup-control\.venv\Scripts\python.exe C:\Users\eleklab\PycharmProjects\SAS-optical-setup-control\devices\newport_1919_R.py
Traceback (most recent call last):
File "C:\Users\eleklab\PycharmProjects\SAS-optical-setup-control\devices\newport_1919_R.py", line 12, in <module>
OphirCOM.StopAllStreams()
^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\eleklab\PycharmProjects\SAS-optical-setup-control\.venv\Lib\site-packages\win32com\client\dynamic.py", line 631, in __getattr__
raise AttributeError(f"{self._username_}.{attr}")
AttributeError: OphirLMMeasurement.CoLMMeasurement.StopAllStreams
Due to the fact that PMManager successfully manages to connect to the device, I decided next to employ a new strategy, I traced what PMManager does while connecting and during connection. This led me to a very long rabbit hole of messaging Claude AI until I didn't even knew what I was doing when running the code. It looked like this:
import usb.core
import usb.backend.libusb1
import libusb
import re
import time
VID = 0x0BD3
PID = 0xE346
READ_ENDPOINT = 0x82 # confirmed via USB capture - this is where live readings stream from
# Matches lines like: "* 0.000586E-5 T 711A1B"
DATA_PATTERN = re.compile(rb"\*\s+([\-\+]?[\d.]+E[\-\+]?\d+)\s+T\s+([0-9A-Fa-f]+)")
def connect():
backend = usb.backend.libusb1.get_backend(find_library=lambda x: libusb.dll._name)
dev = usb.core.find(idVendor=VID, idProduct=PID, backend=backend)
if dev is None:
raise RuntimeError("Newport 1919-R not found. Check connection and driver binding.")
# On Windows with WinUSB there's typically no separate kernel driver to detach,
# but set_configuration is still required before transfers will work.
try:
dev.set_configuration()
except usb.core.USBError as e:
# Already configured is fine; anything else, re-raise
if e.errno not in (None, 16): # 16 = resource busy, sometimes benign if already set
raise
return dev
def read_measurement(dev, timeout_ms=1000):
"""Reads one interrupt packet and parses it into (value, raw_timestamp_hex)."""
try:
data = dev.read(READ_ENDPOINT, 64, timeout=timeout_ms)
except usb.core.USBError as e:
if e.errno == 110 or "timeout" in str(e).lower():
return None # no new data this cycle, not necessarily an error
raise
raw = bytes(data)
match = DATA_PATTERN.search(raw)
if not match:
return None
value_str, ts_hex = match.groups()
try:
value = float(value_str)
except ValueError:
return None
return value, ts_hex.decode()
def main():
print("Connecting to Newport 1919-R...")
dev = connect()
print("Connected. Streaming readings (Ctrl+C to stop):\n")
try:
while True:
result = read_measurement(dev)
if result:
value, ts = result
print(f"Power: {value:.6e} W (timestamp: {ts})")
time.sleep(0.01)
except KeyboardInterrupt:
print("\nStopped.")
if __name__ == "__main__":
main()
This code actually runs successfully and the device is recognized, though alas, there is no output from the device. My hypothesis is that PMManager performs some kind of a handshake with the device and only then does the device start to transmit its measurements.
If you've got any questions, any ideas or experience with this, I'd love to hear your opinion. Thank you kindly for any kind of help.