diff --git a/C sharp/Thorlabs BP209 Beam Profiler 2D Output/Thorlabs.BP2_CSharpDemo/Form1.cs b/C sharp/Thorlabs BP209 Beam Profiler 2D Output/Thorlabs.BP2_CSharpDemo/Form1.cs index 5ccb7f4..36c65fe 100644 --- a/C sharp/Thorlabs BP209 Beam Profiler 2D Output/Thorlabs.BP2_CSharpDemo/Form1.cs +++ b/C sharp/Thorlabs BP209 Beam Profiler 2D Output/Thorlabs.BP2_CSharpDemo/Form1.cs @@ -1,20 +1,22 @@ // Title: BP209 2D Reconstruction C Sharp Example. // Created Date: 2024 - 10 - 12 -// Last modified date: 2024 - 10 - 12 +// Last modified date: 2025 - 11 - 26 // .NET version: 4.8 -// Thorlabs SDK Version: Beam version 9.1.5787.560 +// Thorlabs SDK Version: Beam version 9.3 // Notes: This example is based on the C sharp example which is installed to // C:\Program Files (x86)\IVI Foundation\VISA\WinNT\TLBP2\Examples during software installation. // This example has added the 2D reconstruction algorithm and the reconstructed beam image is displayed. namespace Thorlabs.BP2_CSharpDemo { - using System; + using System; + using System.Data; + using System.Drawing; + using System.Drawing.Imaging; + using System.Runtime.InteropServices; using System.Text; using System.Windows.Forms; - using System.Drawing; using Thorlabs.TLBP2.Interop; - using System.Runtime.InteropServices; /// /// Initializes the form @@ -36,16 +38,6 @@ public partial class Form1 : Form /// private Timer scanTimer = null; - /// - /// array of data structures for each slit. - /// - private bp2_slit_data[] bp2SlitData = new bp2_slit_data[4]; - - /// - /// array of calculation structures for each slit. - /// - private bp2_calculations[] bp2Calculations = new bp2_calculations[4]; - /// /// Initializes a new instance of the class. /// @@ -59,9 +51,6 @@ public Form1() // implementation with driver functions this.ConnectToTheFirstDevice(); - // alternative implementation - ////this.connectToTheFirstDeviceByRM(); - if (this.bp2Device != null) { // get the instrument information @@ -89,7 +78,6 @@ public Form1() ushort sampleCount; double sampleResolution; this.status = this.bp2Device.clear_drum_speed_offset(); - this.status = this.bp2Device.set_drum_speed(10.0); this.status = this.bp2Device.set_drum_speed_ex(10.0, out sampleCount, out sampleResolution); // activate the position correction to have the same calculation results as the Thorlabs Beam Application @@ -101,11 +89,8 @@ public Form1() // activate the drum speed correction this.status = this.bp2Device.set_speed_correction(true); - // use the offset for 10Hz to be compatible with the release version 5.0 - this.status = this.bp2Device.set_reference_position(0, 4, 100.0); - this.status = this.bp2Device.set_reference_position(1, 4, -100.0); - this.status = this.bp2Device.set_reference_position(2, 4, 100.0); - this.status = this.bp2Device.set_reference_position(3, 4, -100.0); + // return all position coordinates from -4500 µm to 4500 µm and flip the x scans + this.status = this.bp2Device.setThorlabsBeamCompatibleCoordinateSystem(true); // poll for a valid scan this.scanTimer = new Timer(); @@ -152,22 +137,6 @@ private void ConnectToTheFirstDevice() } } - /// - /// search for connected devices and connect to the first one. - /// Use the VISA resource manager and simple data types. - /// - private void ConnectToTheFirstDeviceByRM() - { - // get the resource string of the first device - string[] bp2Resources = BP2_ResourceManager.FindRscBP2(); - - if (bp2Resources.Length > 0) - { - // connect to the first device - this.bp2Device = new TLBP2(bp2Resources[0], false, false); - } - } - /// /// poll for a new measurement and fill the structures with the calculation results. /// @@ -177,7 +146,7 @@ private void GetMeasurement() double drumSpeed; try { - if (0 == this.bp2Device.get_drum_speed(out drumSpeed)) + if (0 == this.bp2Device.get_averaged_drum_speed(out drumSpeed)) { this.textBox_drumSpeed.Text = drumSpeed.ToString("f2"); } @@ -200,70 +169,87 @@ private void GetMeasurement() else if ((deviceStatus & 2) == 2) this.toolStripStatusLabel1.Text = "Instrument is ready"; } - - // the gain and drum speed will be corrected during the measurement + double power; - float powerWindowSaturation; - if ((deviceStatus & 1) == 1 && - 0 == this.bp2Device.get_slit_scan_data(this.bp2SlitData, this.bp2Calculations, out power, out powerWindowSaturation, null)) + float powerSaturation; + ushort peakIndex1, peakIndex2,sampleCount1,sampleCount2,centroidIndexSlit1, centroidIndexSlit2; + float peakPositionSlit1, peakPositionSlit2, centroidPositionSlit1, centroidPositionSlit2, peakIntensitySlit1, peakIntensitySlit2; + float darkLevelSlit1, darkLevelSlit2; + + if ((deviceStatus & 1) == 1 && 0 == this.bp2Device.request_scan_data(out power, out powerSaturation, null)) { - this.textBox_peakPositionSlit1.Text = this.bp2Calculations[0].PeakPosition.ToString("f2"); - this.textBox_peakIntensitySlit1.Text = (this.bp2Calculations[0].PeakIntensity * 100.0f / ((float)0x7AFF - this.bp2SlitData[0].SlitDarkLevel)).ToString("f2"); - this.textBox_centroidPositionSlit1.Text = this.bp2Calculations[0].CentroidPos.ToString("f2"); + // get the peak position and centriod position + + this.bp2Device.get_slit_peak(0, out peakIndex1, out peakPositionSlit1, out peakIntensitySlit1); + this.bp2Device.get_slit_peak(1, out peakIndex2, out peakPositionSlit2, out peakIntensitySlit2); + this.bp2Device.get_scan_data_information(0, out sampleCount1, out darkLevelSlit1); + this.bp2Device.get_scan_data_information(1, out sampleCount2, out darkLevelSlit2); + this.bp2Device.get_slit_centroid(0, out centroidIndexSlit1,out centroidPositionSlit1); + this.bp2Device.get_slit_centroid(1, out centroidIndexSlit2, out centroidPositionSlit2); + + this.textBox_peakPositionSlit1.Text = peakPositionSlit1.ToString("f2"); + this.textBox_peakIntensitySlit1.Text = (peakIntensitySlit1 * 100.0f / ((float)0x7AFF - darkLevelSlit1)).ToString("f2"); + this.textBox_centroidPositionSlit1.Text = centroidPositionSlit1.ToString("f2"); - this.textBox_peakPositionSlit2.Text = this.bp2Calculations[1].PeakPosition.ToString("f2"); - this.textBox_peakIntensitySlit2.Text = (this.bp2Calculations[1].PeakIntensity * 100.0f / ((float)0x7AFF - this.bp2SlitData[1].SlitDarkLevel)).ToString("f2"); - this.textBox_centroidPositionSlit2.Text = this.bp2Calculations[1].CentroidPos.ToString("f2"); + this.textBox_peakPositionSlit2.Text = peakPositionSlit2.ToString("f2"); + this.textBox_peakIntensitySlit2.Text = (peakIntensitySlit2 * 100.0f / ((float)0x7AFF - darkLevelSlit2)).ToString("f2"); + this.textBox_centroidPositionSlit2.Text = centroidPositionSlit2.ToString("f2"); - this.textBox_powerSaturation.Text = (powerWindowSaturation*100.0).ToString("f2"); + this.textBox_powerSaturation.Text = (powerSaturation*100.0).ToString("f2"); - this.chart25um.Series[0].Points.DataBindXY(bp2SlitData[0].SlitSamplesPositions, bp2SlitData[0].SlitSamplesIntensities); - this.chart25um.Series[1].Points.DataBindXY(bp2SlitData[1].SlitSamplesPositions, bp2SlitData[1].SlitSamplesIntensities); - this.chart5um.Series[0].Points.DataBindXY(bp2SlitData[2].SlitSamplesPositions, bp2SlitData[2].SlitSamplesIntensities); - this.chart5um.Series[1].Points.DataBindXY(bp2SlitData[3].SlitSamplesPositions, bp2SlitData[3].SlitSamplesIntensities); + GetChartAnd2DReconstruction(); - //Calculate and display the 2D reconstruction image - Get2DReconstruction(); } } /// /// Calculate the 2D reconstructed beam intensity distribution and display the image on the WinForm /// - private void Get2DReconstruction() + private void GetChartAnd2DReconstruction() { - double[] sampleIntensitiesX = new double[7500]; - double[] sampleIntensitiesY = new double[7500]; - double[] samplePositionX = new double[7500]; - double[] samplePositionY = new double[7500]; - double[] gaussianFitIntensitiesX = new double[7500]; - double[] gaussianFitIntensitiesY = new double[7500]; - double power; - float powerSaturation; + double[] sampleIntensities25umX = new double[7500]; + double[] sampleIntensities25umY = new double[7500]; + double[] samplePosition25umX = new double[7500]; + double[] samplePosition25umY = new double[7500]; + double[] sampleIntensities5umX = new double[7500]; + double[] sampleIntensities5umY = new double[7500]; + double[] samplePosition5umX = new double[7500]; + double[] samplePosition5umY = new double[7500]; + double[] gaussianFitIntensities25umX = new double[7500]; + double[] gaussianFitIntensities25umY = new double[7500]; float temp; - - //Request the scan data - this.bp2Device.request_scan_data(out power,out powerSaturation,null); + //Get the intensities from the 25um X slit and the 25um Y slit - this.bp2Device.get_sample_intensities(0, sampleIntensitiesX, samplePositionX); - this.bp2Device.get_sample_intensities(1, sampleIntensitiesY, samplePositionY); + this.bp2Device.get_sample_intensities(0, sampleIntensities25umX, samplePosition25umX); + this.bp2Device.get_sample_intensities(1, sampleIntensities25umY, samplePosition25umY); + + //Get the intensities from the 5um X slit and the 5um Y slit + this.bp2Device.get_sample_intensities(2, sampleIntensities5umX, samplePosition5umX); + this.bp2Device.get_sample_intensities(3, sampleIntensities5umY, samplePosition5umY); + + //Chart display + this.chart25um.Series[0].Points.DataBindXY(samplePosition25umX, sampleIntensities25umX); + this.chart25um.Series[1].Points.DataBindXY(samplePosition25umY, sampleIntensities25umY); + this.chart5um.Series[0].Points.DataBindXY(samplePosition5umX, sampleIntensities5umX); + this.chart5um.Series[1].Points.DataBindXY(samplePosition5umY, sampleIntensities5umY); //Get the gaussian fit intensites from the 25um X slit and the 25um Y slit - this.bp2Device.get_slit_gaussian_fit(0, out temp,out temp,out temp,gaussianFitIntensitiesX); - this.bp2Device.get_slit_gaussian_fit(1, out temp, out temp, out temp, gaussianFitIntensitiesY); + this.bp2Device.get_slit_gaussian_fit(0, out temp,out temp,out temp, gaussianFitIntensities25umX); + this.bp2Device.get_slit_gaussian_fit(1, out temp, out temp, out temp, gaussianFitIntensities25umY); //2D reconstruction - double[,] imageData = new double[750, 750]; + int imageSize = 750; + double[,] imageData = new double[imageSize, imageSize]; double imageDataMax = 0; int ixz, iyz; - for (int ix = 0; ix < 750; ix++) + for (int ix = 0; ix < imageSize; ix++) { - for (int iy = 0; iy < 750; iy++) + for (int iy = 0; iy < imageSize; iy++) { //2D reconstruction algorithm - ixz = (750 - ix - 1) * 10; - iyz = (750 - iy - 1) * 10; - imageData[ix, iy] = sampleIntensitiesX[ixz] * gaussianFitIntensitiesX[ixz] * sampleIntensitiesY[iyz] * gaussianFitIntensitiesY[iyz]; + ixz = (imageSize - ix - 1) * 7500 / imageSize; + iyz = (imageSize - iy - 1) * 7500 / imageSize; + imageData[ix, iy] = sampleIntensities25umX[ixz] * gaussianFitIntensities25umX[ixz] * sampleIntensities25umY[iyz] * gaussianFitIntensities25umY[iyz]; //set the negative values to zero if (imageData[ix, iy] < 0) @@ -279,22 +265,29 @@ private void Get2DReconstruction() } //Normalize intensity values and generate the Bitmap image - int imageGrayValue; - Bitmap bitmap = new Bitmap(750, 750); - for (int x = 0; x < 750; x++) + Bitmap bitmap = new Bitmap(imageSize, imageSize, PixelFormat.Format24bppRgb); + BitmapData bmpData = bitmap.LockBits( + new Rectangle(0, 0, imageSize, imageSize),ImageLockMode.WriteOnly,bitmap.PixelFormat); + + int stride = bmpData.Stride; + byte[] pixelData = new byte[stride * imageSize]; + + for (int y = 0; y < imageSize; y++) { - for (int y = 0; y < 750; y++) + for (int x = 0; x < imageSize; x++) { - imageGrayValue = (int)(imageData[x, y] * 255 / imageDataMax); - Color color = Color.FromArgb(imageGrayValue, imageGrayValue, imageGrayValue); - bitmap.SetPixel(x, y, color); + int gray = (int)(imageData[x, y] * 255 / imageDataMax); + gray = Math.Min(255, Math.Max(0, gray)); + int pixelIndex = y * stride + x * 3; + pixelData[pixelIndex + 0] = (byte)gray; + pixelData[pixelIndex + 1] = (byte)gray; + pixelData[pixelIndex + 2] = (byte)gray; } } - // set the bitmap to the Image property + Marshal.Copy(pixelData, 0, bmpData.Scan0, pixelData.Length); + bitmap.UnlockBits(bmpData); this.reconstructionPicture.Image = bitmap; - - } /// /// If a new scan is available, get the data from the instrument and display the calculation results on the form. @@ -329,5 +322,5 @@ private void Form1_FormClosing(object sender, FormClosingEventArgs e) this.bp2Device.Dispose(); } } - } + } } diff --git a/C++/BP209 2D Output/BP209_2D_output.cpp b/C++/BP209 2D Output/BP209_2D_output.cpp index 7f52170..fc0df71 100644 --- a/C++/BP209 2D Output/BP209_2D_output.cpp +++ b/C++/BP209 2D Output/BP209_2D_output.cpp @@ -1,7 +1,8 @@ -//Example Date of Creation(YYYY - MM - DD) 2024 - 04 - 24 -//Example Date of Last Modification on Github 2024 - 04 - 24 +//Example Date of Creation(YYYY - MM - DD) 2024 - 04 - 24 +//Example Date of Last Modification on Github 2025 - 11 - 05 //Version of C++ used for Testing and IDE: C++ 14, Visual Studio 2022 -//Version of the Thorlabs SDK used : Beam version 9.1.5787.560 +//Version of OpenCV: OpenCV 4.12.0 +//Version of the Thorlabs SDK used : Beam version 9.3 //Example Description: The sample code shows how to control a BP209 beam profiler in C++. //In the example the available beam profilers are found, a connection is established, several parameters are set, //several output values are displayed and a 2D image is shown. @@ -18,7 +19,7 @@ using namespace cv; // forward declaration void print_error_msg(ViStatus err); void Beam_Profile_Reconstruction(); -ViSession m_instrumentHandle; +ViSession m_instrumentHandle = 0; //set the measured laser wavelengh unit: nm double wavelength = 633; @@ -55,50 +56,74 @@ int main(int argc, char* argv) return 0; } + ViChar modelName[256]; + ViChar serialNo[256]; + ViChar manufacturer[256]; + ViBoolean isAvailable; + res = TLBP2_getRsrcInfo(0, 0, modelName, serialNo, manufacturer, &isAvailable); + // connect with the first device res = TLBP2_init(resStr[0].resourceString, VI_TRUE, VI_TRUE, &m_instrumentHandle); - if ((res & _VI_ERROR) > 0) + if (res != VI_SUCCESS) { print_error_msg(res); return 0; } - char serialNo[128]; - res = TLBP2_get_serial_number(m_instrumentHandle, serialNo); - printf("%s is connected. \n", serialNo); + + if (isAvailable) + { + printf("%s (SN: %s) is connected. \n", modelName, serialNo); + } + else + { + printf("%s (SN: %s) is not available.\n", modelName, serialNo); + // release the device + TLBP2_close(m_instrumentHandle); + return 0; + } + // release the buffer for the resource strings delete[] resStr; //set auto gain res = TLBP2_set_auto_gain(m_instrumentHandle, VI_TRUE); - if ((res & _VI_ERROR) > 0) + if (res != VI_SUCCESS) { print_error_msg(res); + // release the device + TLBP2_close(m_instrumentHandle); return 0; } //set bandwidth ViReal64 bw_buffer[4] = { 125,125,125,125 }; res = TLBP2_set_bandwidths(m_instrumentHandle, bw_buffer); - if ((res & _VI_ERROR) > 0) + if (res != VI_SUCCESS) { print_error_msg(res); + // release the device + TLBP2_close(m_instrumentHandle); return 0; } //set wavelength res = TLBP2_set_wavelength(m_instrumentHandle, wavelength); - if ((res & _VI_ERROR) > 0) + if (res != VI_SUCCESS) { print_error_msg(res); + // release the device + TLBP2_close(m_instrumentHandle); return 0; } //set power factor res = TLBP2_set_user_power_factor(m_instrumentHandle, powerCorrectionFactor); - if ((res & _VI_ERROR) > 0) + if (res != VI_SUCCESS) { print_error_msg(res); + // release the device + TLBP2_close(m_instrumentHandle); return 0; } @@ -113,22 +138,26 @@ int main(int argc, char* argv) res = TLBP2_set_scanning_method(m_instrumentHandle, 1, scanningMethod); res = TLBP2_set_scanning_method(m_instrumentHandle, 2, scanningMethod); res = TLBP2_set_scanning_method(m_instrumentHandle, 3, scanningMethod); - if ((res & _VI_ERROR) > 0) + if (res != VI_SUCCESS) { print_error_msg(res); + // release the device + TLBP2_close(m_instrumentHandle); return 0; } } else { - printf("Invalid Input! The scanning method is set to slit scanning mode.\n"); + printf("Invalid Input! The scanning method is set to slit scanning method.\n"); res = TLBP2_set_scanning_method(m_instrumentHandle, 0, 0); res = TLBP2_set_scanning_method(m_instrumentHandle, 1, 0); res = TLBP2_set_scanning_method(m_instrumentHandle, 2, 0); res = TLBP2_set_scanning_method(m_instrumentHandle, 3, 0); - if ((res & _VI_ERROR) > 0) + if (res != VI_SUCCESS) { print_error_msg(res); + // release the device + TLBP2_close(m_instrumentHandle); return 0; } } @@ -140,25 +169,41 @@ int main(int argc, char* argv) res = TLBP2_set_drum_speed_ex(m_instrumentHandle, 10, &sampleCount, &resolution); else //knife edge mode res = TLBP2_set_drum_speed_ex(m_instrumentHandle, 2, &sampleCount, &resolution); - if ((res & _VI_ERROR) > 0) + if (res != VI_SUCCESS) { print_error_msg(res); + // release the device + TLBP2_close(m_instrumentHandle); return 0; } //set position correction res = TLBP2_set_position_correction(m_instrumentHandle, VI_TRUE); - if ((res & _VI_ERROR) > 0) + if (res != VI_SUCCESS) + { + print_error_msg(res); + // release the device + TLBP2_close(m_instrumentHandle); + return 0; + } + + //return all position coordinates from -4500 µm to 4500 µm and flip the x scans + res = TLBP2_setThorlabsBeamCompatibleCoordinateSystem(m_instrumentHandle, VI_TRUE); + if (res != VI_SUCCESS) { print_error_msg(res); + // release the device + TLBP2_close(m_instrumentHandle); return 0; } //set speed correction res = TLBP2_set_speed_correction(m_instrumentHandle, VI_TRUE); - if ((res & _VI_ERROR) > 0) + if (res != VI_SUCCESS) { print_error_msg(res); + // release the device + TLBP2_close(m_instrumentHandle); return 0; } @@ -169,12 +214,10 @@ int main(int argc, char* argv) res = TLBP2_get_device_status(m_instrumentHandle, &device_status); } - static BP2_SLIT_DATA slit_data[4], slit_data_kinfe[4]; /// BP2_MAX_SLIT_COUNT = 4 - static BP2_CALCULATIONS calculation_result[4], calculation_result_knife[4]; static ViReal64 power_intensities[7500]; static ViBoolean slit_indices[4] = { VI_TRUE, VI_TRUE, VI_TRUE, VI_TRUE}; ViReal64 power; - ViReal32 powerSaturation; + ViReal32 powerSaturation,centroidPositionX, centroidPositionY,gaussianDiameterX,gaussianDiameterY; ViUInt8 gain[4]; ViUInt8 gainPower; @@ -183,7 +226,7 @@ int main(int argc, char* argv) printf("Adjusting Gain...\n"); for (int i = 0; i < 10; i++) { - res = TLBP2_get_slit_scan_data(m_instrumentHandle, slit_data, calculation_result, &power, &powerSaturation, power_intensities); + res = TLBP2_request_scan_data(m_instrumentHandle, &power, &powerSaturation, power_intensities); res = TLBP2_get_gains(m_instrumentHandle, gain, &gainPower); printf("Gain:\n"); printf(" 25um slit x: %d, 25um slit y: %d\n", gain[0],gain[1]); @@ -195,19 +238,25 @@ int main(int argc, char* argv) if (scanningMethod == 0)//slit scanning mode { //Get the slit scan data - res = TLBP2_get_slit_scan_data(m_instrumentHandle, slit_data, calculation_result, &power, &powerSaturation, power_intensities); + res = TLBP2_request_scan_data(m_instrumentHandle, &power, &powerSaturation, power_intensities); if (res == VI_SUCCESS) { + TLBP2_get_slit_centroid(m_instrumentHandle, 2, VI_NULL, ¢roidPositionX); + TLBP2_get_slit_centroid(m_instrumentHandle, 3, VI_NULL, ¢roidPositionY); + TLBP2_get_slit_gaussian_fit(m_instrumentHandle, 2, VI_NULL, &gaussianDiameterX, VI_NULL, VI_NULL); + TLBP2_get_slit_gaussian_fit(m_instrumentHandle, 3, VI_NULL, &gaussianDiameterY, VI_NULL, VI_NULL); printf("Corrected Power value: %.2f mW\n", power); - printf("5um Slit X Centroid Position: %.2f\n", calculation_result[2].centroidPosition); - printf("5um Slit Y Centroid Position: %.2f\n", calculation_result[3].centroidPosition); - printf("5um Slit X Gaussian Fit Diameter: %.2f\n", calculation_result[2].gaussianFitDiameter); - printf("5um Slit Y Gaussian Fit Diameter: %.2f\n", calculation_result[3].gaussianFitDiameter); + printf("5um Slit X Centroid Position: %.2f\n", centroidPositionX); + printf("5um Slit Y Centroid Position: %.2f\n", centroidPositionY); + printf("5um Slit X Gaussian Fit Diameter: %.2f\n", gaussianDiameterX); + printf("5um Slit Y Gaussian Fit Diameter: %.2f\n", gaussianDiameterY); Beam_Profile_Reconstruction(); } } else//knife edge mode { + static BP2_SLIT_DATA slit_data[4], slit_data_kinfe[4]; /// BP2_MAX_SLIT_COUNT = 4 + static BP2_CALCULATIONS calculation_result[4], calculation_result_knife[4]; //Calculate the knife edge data from the slit data. res = TLBP2_get_slit_scan_data(m_instrumentHandle, slit_data, calculation_result, &power, &powerSaturation, power_intensities); res = TLBP2_get_knife_edge_reconstruction(m_instrumentHandle, slit_data, calculation_result, slit_indices, slit_data_kinfe, calculation_result_knife); @@ -241,8 +290,6 @@ void Beam_Profile_Reconstruction() static ViReal64 gaussianFitIntensitiesY[7500]; ViReal32 gaussianFitPercentageX, gaussianFitPercentageY; - //Request the scan data - TLBP2_request_scan_data(m_instrumentHandle, VI_NULL, VI_NULL, VI_NULL); //Get the intensities from the 5um X slit and the 5um Y slit res = TLBP2_get_sample_intensities(m_instrumentHandle, 2, sampleIntensitiesX, samplePositionsX); res = TLBP2_get_sample_intensities(m_instrumentHandle, 3, sampleIntensitiesY, samplePositionsY); @@ -314,10 +361,12 @@ void Beam_Profile_Reconstruction() reconstructionImage.at(col, row) = (uchar)(255 * intensityTemp[row][col] / intensityMax); } } + imshow("X Intensity", IntensityXImage); imshow("X Gaussian Fit Intensity", GaussianXImage); imshow("2D Reconstruction", reconstructionImage); - waitKey(0); + + waitKey(8000); } // prints the error message from an error code diff --git a/Matlab/CCT Spectrometers/CCT_example.m b/Matlab/CCT Spectrometers/CCT_example.m new file mode 100644 index 0000000..27b8e61 --- /dev/null +++ b/Matlab/CCT Spectrometers/CCT_example.m @@ -0,0 +1,76 @@ +%% Header +% Title: CCT_example.m +% Created Date: 2025-12-10 +% Last modified date: 2025-12-10 +% Matlab Version:R2023a +% Thorlabs DLL version:1.0.20.4045 +%% Notes: The example shows how to connect to a CCT spectrometer, set the exposure time and acquire a spectrum +% Tested with CCT11 +% + +% Load the Compact Spectrometer SDK DLLs +dll_path='C:\Program Files\Thorlabs\ThorSpectra'; +NET.addAssembly(fullfile(dll_path, 'Thorlabs.ManagedDevice.CompactSpectrographDriver.dll')); +NET.addAssembly(fullfile(dll_path, 'Thorlabs.ManagedDevice.dll')); +NET.addAssembly(fullfile(dll_path, 'Microsoft.Extensions.Logging.Abstractions.dll')); +NET.addAssembly('System.Runtime'); + +import Thorlabs.ManagedDevice.CompactSpectrographDriver.Workflow.StartupHelperCompactSpectrometer.*; +import Thorlabs.ManagedDevice.Trace.ExampleLogger.*; +import Microsoft.Extensions.Logging.Abstractions.*; +import Thorlabs.ManagedDevice.CompactSpectrographDriver.ICompactSpectrographDriver.*; + +logger = Thorlabs.ManagedDevice.Trace.ExampleLogger('ML',Microsoft.Extensions.Logging.LogLevel.Trace,1==1,'MatLab'); +startupHelper = Thorlabs.ManagedDevice.CompactSpectrographDriver.Workflow.StartupHelperCompactSpectrometer(logger); + +cts = System.Threading.CancellationTokenSource(); +cancellationToken = cts.Token; + +%find devices +discoveredDevicestask=startupHelper.GetKnownDevicesAsync(cancellationToken); +discoveredDevicestask.Wait(); +discoveredDevices=discoveredDevicestask.Result; + +for i=0:discoveredDevices.Count-1 + disp("discovered devices:") + disp(discoveredDevices.Item(i)); +end + +%if spectrometers are found +if(discoveredDevices.Count>0) + + disp("connecting to first device ...") + + Deviceid=discoveredDevices.Item(0); + + spectrometer=startupHelper.GetCompactSpectrographById(Deviceid); + + %set exposure time + exposure=500; %exposure time in milliseconds + exposure_result=spectrometer.SetManualExposureAsync(exposure,cancellationToken).Result; + if exposure_result==1 + disp(['exposure time is set to ',num2str(exposure),' ms']); + end + + %acquire spectrum + spectrumtask=spectrometer.AcquireSingleSpectrumAsync(cancellationToken); + spectrumtask.Wait(); + spectrum=spectrumtask.Result; + + %dispose startup helper + startupHelper.Dispose(); + + %plot spectrum + figure; plot(spectrum.Wavelength,spectrum.Intensity) + + +else + disp("No CCT spectrometer connected") +end + + + + + + + diff --git a/Matlab/Powermeter/PowermeterAppExample.mlapp b/Matlab/Powermeter/PowermeterAppExample.mlapp new file mode 100644 index 0000000..3d55b5e Binary files /dev/null and b/Matlab/Powermeter/PowermeterAppExample.mlapp differ diff --git a/Matlab/Powermeter/README.md b/Matlab/Powermeter/README.md index 0371422..317e288 100644 --- a/Matlab/Powermeter/README.md +++ b/Matlab/Powermeter/README.md @@ -5,7 +5,7 @@ This example shows how to initialize Thorlabs Power Meters that utilize the TLPM The code uses the .NET DLL file for the power meters TLPMX_64.Interop.dll. -### Prerequisites -Make sure to have the latest install of Thorlabs OPM and make sure the file location on line 10 leads directly to the TLPMX dll. +### Power Meter App +This example shows how to use a power meter with a Matlab App. diff --git a/Python/Thorlabs PMxxx Power Meters/SCPI/Readme.md b/Python/Thorlabs PMxxx Power Meters/SCPI/Readme.md index f3297dc..7611e54 100644 --- a/Python/Thorlabs PMxxx Power Meters/SCPI/Readme.md +++ b/Python/Thorlabs PMxxx Power Meters/SCPI/Readme.md @@ -27,7 +27,7 @@ For closer details refer to [Readme](scopeMode). Available for PM6x, PM103, PM10 Minimal template script ```PMxxx_SCPI_OpenAnyvisa.py``` to open a known instrument resource using anyvisa library. ### Search Anyvisa -Minimal template script ```PMxxx_SCPI_SearchAnyvisa.py``` to run instrument search and open one of the devices found using anvisa library. +Minimal template script ```PMxxx_SCPI_SearchAnyvisa.py``` to run instrument search and open one of the devices found using anyvisa library. ## SCPI Command documentation For most of the Thorlabs Powermeter there is a detail [SCPI command documentation](commandDocu) in .html file format available. @@ -52,9 +52,10 @@ python -m pip install anyvisa*.whl ### National Instruments :tm: Visa -If you want to control the Power Meter on Linux, with pyvisa library or with SCPI commands within your CVI or LabView application, -you have to install National Instruments :tm: Visa Runtime (May be installed already if you installed NI LabView or NI CVI). -Once installed you must switch the driver for the Power Meter manually by using Thorlabs Driver Switcher or Windows +If you want to control the Power Meter with the pyvisa library and with SCPI commands, +you have to install National Instruments :tm: Visa Runtime (May be installed already if you installed NI LabView or NI CVI). +This works also on many Linux systems. +On Windows, you must then switch the driver for the Power Meter manually by using Thorlabs Driver Switcher or Windows Device Manager (Experts only). Once the runtime is installed and driver has been switched you can install pyvisa python library via command. @@ -62,4 +63,63 @@ via command. python -m pip install pyvisa ``` -Note: pyvisa does communicate with Thorlabs Ethernet or Bluetooth LE device interfaces. +Note: pyvisa does not communicate with Thorlabs Ethernet or Bluetooth LE device interfaces. + +### Raspberry Pi 4 +On Raspberry Pi with ARM Linux, both TLVisa and NI Visa do not work. Another approach is using the Pyvisa-Py backend. +We tested the following procedure on Raspberry Pi 4, Linux 13 (Trixie), PM100D3. +``` +sudo apt update && sudo apt upgrade -y +``` +Most Raspberry Pi OS versions come with Python pre-installed. Check with: +``` +python3 --version +``` +If Python is not already installed: +``` +sudo apt install python3 python3-pip -y +``` +If libusb is not installed: +``` +sudo apt install libusb-1.0-0-dev +``` +Install Pyvisa and Pyvisa-Py: +``` +sudo apt install python3-pyvisa +sudo apt install python3-pyvisa-py +sudo apt install python3-usb +sudo apt install zeroconf +``` +In order to get the permission to acces the power meter, create usbgroup and add your user: +``` +sudo groupadd usbgroup +sudo usermod -aG usbgroup $USER +``` +then set a udev rule, open the following file to edit: +``` +sudo nano /etc/udev/rules.d/99-usbgroup.rules +``` +(create directory and file if it does not exist) +Insert the following line into the file +``` +SUBSYSTEM=="usb", GROUP="usbgroup", MODE="0666" +``` +(This is for all USB devices. You can also set more specific rules) +Reload and reboot to activate the rules +``` +sudo udevadm control --reload-rules +sudo udevadm trigger +sudo reboot +``` +Plug in the powermeter and check with lsusb, if the device is present. +Run in Python: +``` +import pyvisa +rm = pyvisa.ResourceManager('@py') +print(rm.list_resources()) +inst = rm.open_resource('USB0::4883::32921::P000000064::0::INSTR')#substitute with the actual resource string that you get from the previous command +print(inst.query('*IDN?')) +print(inst.query('MEAS?')) +``` +Also see the above ```PMxxx_SCPI_pyvisa.py``` +