diff --git a/Modules/+Drivers/@Wavemeter1Ch/Wavemeter1Ch.m b/Modules/+Drivers/@Wavemeter1Ch/Wavemeter1Ch.m new file mode 100644 index 000000000..963b6419f --- /dev/null +++ b/Modules/+Drivers/@Wavemeter1Ch/Wavemeter1Ch.m @@ -0,0 +1,374 @@ +classdef Wavemeter1Ch < Modules.Driver + %WAVEMETER Connects with server.py on host machine to control + % the Wavemeter. For single channel HighFinesse wavemeters. + % See Wavemeter.m for multichannel functionality. + % + % Call with the IP of the host computer + % (singleton based on ip) + % + % + % In following comments, generally: + % channel/port = measurement channel + % signal = signal line from DAC (for ease, client doesn't allow + % changing away from same as channel/port). As in, for + % channel/port 1, client will only allow setting/unsetting signal + % 1. + % DAC == Deviation + + properties(Constant) + hwname = 'wavemeter'; + resolution = 0.00001; %frequency resolution in THz + wlDefaultRange = 6; %(400-1000nm for WS6 wavemeter) + %will need modified to account + %for different wavemeters in the future + end + properties + timeout = 2; %timeout for attempting to read from wavemeter + end + properties(SetAccess=private,Hidden) + connection + end + properties(SetAccess=immutable) + readonly = false; + Channel = 1; + end + methods(Static) + function obj = instance(ip) + mlock; + persistent Objects + if isempty(Objects) + Objects = Drivers.Wavemeter1Ch.empty(1,0); + end + [~,resolvedIP] = resolvehost(ip); + for i = 1:length(Objects) + if isvalid(Objects(i)) && isequal({resolvedIP},Objects(i).singleton_id) + obj = Objects(i); + return + end + end + obj = Drivers.Wavemeter1Ch(ip); + obj.singleton_id = {resolvedIP}; + Objects(end+1) = obj; + end + + + stream(ip,dt) + end + methods(Access=private) + function obj = Wavemeter1Ch(ip) + obj.connection = hwserver(ip); + % Look for existing low signals on other channels + % Not sure if interactive input is useful for anything + %if interactive + %end + %ensure initialized to CW mode + if (obj.getPulseMode == 1) + obj.setPulseMode(0); + end + %ensure initialized to fine precision + if (obj.getWideMode == 1) + obj.setWideMode(0); + end + %ensure initialized to fast measurement mode + if (obj.getFastMode == 0) + obj.setFastMode = 1; + end + %ensure initialized to autoexposure mode + if (obj.getExposureMode == false) + obj.setExposureMode(true); + end + if (obj.getResultMode ~= 0) + obj.setResultMode(0); + % 'cReturnWavelengthVac' = 0 + % 'cReturnWavelengthAir' = 1 + % 'cReturnFrequency' = 2 + % 'cReturnWavenumber' = 3 + % 'cReturnPhotonEnergy' = 4 + end + if ( obj.getRange ~= 6 ) + obj.setRange(obj.wlDefaultRange); + end + end + function response = com(obj,funcname,varargin) %keep this + response = obj.connection.com(obj.hwname,funcname,varargin{:}); + end + function output = measure(obj,cmd) + tstart = tic; + output = -1; % In case obj.timeout = 0 + while toc(tstart) <= obj.timeout && output < 0 + output = obj.com(cmd,obj.Channel,0); + end + if output <= 0 + if output == -1 + msg = 'Low signal'; + elseif output == -2 + msg = 'High signal'; + else + msg = sprintf('Unknown error code: %g',output); + end + ME = MException('WAVEMETER:read','wavemeter timeout on measurement: %s',msg); + throwAsCaller(ME); + end + end + end + methods + function delete(obj) + delete(obj.connection) + end + + %% Measurement + function output = getWavelength(obj) + output = obj.measure('GetWavelengthNum'); + end + function output = getFrequency(obj) + output = obj.measure('GetFrequencyNum'); + end + + %% Global Control Settings + function output = getPIDstatus(obj) + % Note, this applies to all ports with ACTIVE signals + output = obj.com('GetDeviationMode',0); + end + function setPIDstatus(obj,bool,override) + % Note, this applies to all ports with ACTIVE signals + % override is used to make sure old code/users doesn't accidentally call + if nargin < 3 || ~override + answer = questdlg(sprintf('%s\n%s','Are you sure you want to change PID status for all channels?',... + 'Consider turning off just for yours using setDeviationChannel(true|false).'),... + mfilename,'Continue','Cancel','Cancel'); + if strcmp(answer,'Cancel') + return + end + end + if ~obj.getDeviationChannel + warning('DAC Channel currently disabled. Use setDeviationChannel(true) to turn on.') + end + bool = logical(bool); + obj.com('SetDeviationMode',bool); + end + + %% PID Set Point + function output = getPIDtarget(obj) + % Units are whatever is returned by getPIDunits + eq = obj.com('GetPIDCourseNum',obj.Channel,0); + output = str2num(eq); %#ok % PIDtarget can be equations; attempt to eval + assert(~isempty(output),sprintf('Could not evaluate PIDtarget: %s',eq)); + end + function setPIDtarget(obj,val) + if ~obj.getDeviationChannel + warning('DAC Channel currently disabled. Use setDeviationChannel(true) to turn on.') + end + obj.com('SetPIDCourseNum',obj.Channel,num2str(val,'%0.7f')); %set with 0.1 MHz precision + end + function units = getPIDunits(obj) + unit_types = {'nm','nm_air','THz','1/cm','eV'}; + units = obj.com('GetPIDSetting','cmiDeviationUnit',obj.Channel); + units = unit_types{units.val+1}; + end + function setPIDunits(obj,unit) + % Unit can be a string in unit_types or an index (from 1) + unit_types = {'nm','nm_air','THz','1/cm','eV'}; + if all(ischar(unit)) + unit = ismember(unit_types,unit); + assert(sum(unit)==1,sprintf('%s not allowed unit: %s',unit,strjoin(unit_types,', '))); + unit = find(unit); + else + assert(unit <= length(unit_types) && unit > 0,'Unit index out of range') + end + obj.com('SetPIDSetting','cmiDeviationUnit',obj.Channel,unit-1); % DLL indexes this from 0 + end + + function ClearPIDHistory(obj) + obj.com('ClearPIDHistory',obj.Channel); + end + + %% DAC Voltage + function ch = getDeviationChannel(obj) + % This is the "signal" for the given port (aka channel) + % Returns true if set, false if not + ch = obj.com('GetPIDSetting','cmiDeviationChannel',obj.Channel); + ch = logical(ch.val); + end + function setDeviationChannel(obj,state) + % This is the "signal" for the given port (aka channel) + % Either false if not set, or true if on + % Note this function only allows setting and removing + if state % Keep it same as channel number + if ~obj.getPIDstatus % If "global" PID is off, prompt to enable + answer = questdlg(['PID regulation is off for all signals, ',... + 'so setting Deviation Channel will not do anything ',... + 'unless PID regulation is enabled.',newline, newline,... + 'Do you want to enable PID regulation now?',newline,... + '(note this might impact other users)'],... + mfilename,'Yes','No','No'); + if strcmp(answer,'Yes') + obj.com('SetDeviationMode','1'); % Manually to avoid additional queries in setPIDstatus + else + warning('Note, PID regulation is off. Call setPIDstatus(true) to enable.') + end + end + obj.com('SetPIDSetting','cmiDeviationChannel',obj.Channel,obj.Channel); + else + obj.com('SetPIDSetting','cmiDeviationChannel',obj.Channel,0); + end + end + + function output = getDeviationVoltage(obj) + % Output in volts + assert(logical(obj.getDeviationChannel),'DAC signal is disabled, thus can be set but not read. Use setDeviationChannel(true) to turn on.') + output = obj.com('GetDeviationSignalNum',obj.Channel,0)/1000; + end + function setDeviationVoltage(obj,volts) + % Input in volts + assert(~obj.getPIDstatus()||~obj.getDeviationChannel,'Cannot set while PID is enabled and channel is active.') + mv = round(volts*1000); + %if (volts == 0) + % mv = 0; + %end + obj.com('SetDeviationSignalNum',obj.Channel,mv); + end + + + %% Camera control + function output = getExposureMode(obj) + % true for auto exposure, false for manual + output = obj.com('GetExposureModeNum',obj.Channel,0); + end + function setExposureMode(obj,bool) + % true for auto exposure, false for manual + bool = logical(bool); + obj.com('SetExposureModeNum',obj.Channel,bool); + end + function val = getExposure(obj) + val = obj.com('GetExposure',0); + end + function val = getPower(obj) + val = obj.com('GetPowerNum',obj.Channel,0); + end + function val = setExposure(obj,val) + obj.com('SetExposure',val); + end + + function lw = getLinewidth(obj) %66 + lw = obj.com('GetLinewidth','cReturnFrequency'); + %requires R or L version wavemeters + %that can measure linewidths. + end + function meas_mode = getOperationState(obj) %75 + meas_mode = obj.com('GetOperationState'); + % Mode: Adjusting = 'cAdjustment' + % Measuring = 'cMeasurement' + % Stopped = 'cStop' + end + function stop_measurement(obj) + obj.com('Operation','cCtrlStopAll') + end + function start_measurement(obj) + obj.com('Operation','cCtrlStartMeasurement') + %obj.com('Operation','cCtrlStartRecord') + end + function calibration(obj,val,cal_wl) + switch nargin + case 0 + val = 'cNEL'; + % cal_wl = ...; + case 1 + % cal_wl= ...; + end + %what is the calibration wavelength for the CNEL Neon lamp? + obj.com('Calibration', val, 'cReturnWavelengthVac',cal_wl,1) %76 + end + %function triggerMeasurement %77 + %end + function setBackground(obj,val) %78 + % 1 activates background consideration + % 0 deactivates it + obj.com('SetBackground',val) + end + function ResultMode = getResultMode(obj) %79 + ResultMode = obj.com('GetResultMode',0); + end + function setResultMode(obj, val) %79 + %Allowable Modes: + % 'cReturnWavelengthVac' = 0 + % 'cReturnWavelengthAir' = 1 + % 'cReturnFrequency' = 2 + % 'cReturnWavenumber' = 3 + % 'cReturnPhotonEnergy' = 4 + obj.com('SetResultMode',val) + end + function wl_Range = getRange(obj) %79 + wl_Range = obj.com('GetRange',0); + %Returns active wavelength range int + % returns 6 for 400-1000nm range on WS6 + % returns 7 for 1000-17000 nm range on WS6 + end + function setRange(obj,val) %79 + %Allowable Values for WS6 + obj.com('SetRange', val) + end + function PulseMode = getPulseMode(obj) %80 + %Returns 1 if in pulse mode and a 0 if + %in CW mode. + PulseMode = obj.com('GetPulseMode',0); + end + function setPulseMode(obj,val) %81 + %Allowable values + % CW mode: val = 1 + % Pulsed Mode: val = 0 + obj.com('SetPulseMode',val) + end + function precisionMode = getWideMode(obj) %81 + %sets measurement precision + % returns 1 for coarse resolution + % returns 0 for fine resolution (full precision) + precisionMode = obj.com('GetWideMode',0); + end + function setWideMode(obj, val) %81 + %Allowable values + % coarse precision: val = 1 + % full precision: val = 0 + obj.com('SetWideMode',val); + end + function FastMode = getFastMode(obj) %82 + % returns 1 for faster calculation by fuzzy drawing + % of interference patterns + % returns 0 for exact drawing. + FastMode = obj.com('GetFastMode',0); + end + function setFastMode(obj, val) %82 + %Allowable values + % fast mode: val = 1 + % slow slow: val = 0 + obj.com('SetFastMode',val) + end + function bg = getBackground(obj) %83 + % returns 1 when bg is loaded and is used to correct signal + % returns 0 otherwise + bg = obj.com('GetBackground'); + end + function lwMode = getLinewidthMode(obj) %84 + % returns 1 if linewidth mode is available + % returns 0 otherwise + lwMode = obj.com('GetLinewidthMode',0); + end + function calMode = getAutoCalMode(obj) %86 + % returns 1 if in autocal mode + % returns 0 otherwise + calMode = obj.com('GetAutoCalMode',0); + end + function setAutoCalMode(obj,val) %86 + %Allowable values + % Autocal on: val = 1 + % Autocal off: val = 0 + obj.com('SetAutoCalMode',val); + end + %function getAutoCalSetting %87 + %end + %function setAutoCalSetting %88 + %end + + + + end +end \ No newline at end of file diff --git a/Modules/+Drivers/@Wavemeter1Ch/stream.m b/Modules/+Drivers/@Wavemeter1Ch/stream.m new file mode 100644 index 000000000..e3925f8d8 --- /dev/null +++ b/Modules/+Drivers/@Wavemeter1Ch/stream.m @@ -0,0 +1,101 @@ +function stream(ip,dt) +% WAVEMETERSTREAM Streams all active channels from wavemeter +% Includes wavelength and DAC voltage as function of time +% Channel number in legend will be red if locking is set to on, black +% otherwise. + +if nargin < 2 + dt = 0.2; +end + +f = UseFigure(mfilename,'name','Wavemeter Monitor','NumberTitle','off','MenuBar','none'); +if ~isempty(f.UserData) && isfield(f.UserData,'timer') && isvalid(f.UserData.timer) % Means currently running version + stop(f.UserData.timer); + clf(f,'reset'); +end + +% Get all available channels (should be readonly) +%states = find(Drivers.Wavemeter.getChannelStates(ip)); +%for i = 1:length(states) +wm = Drivers.Wavemeter1Ch.instance(ip); + +ax(1) = subplot(3,1,1,'parent',f); hold(ax(1),'on'); +xlabel(ax(1),'Time (s)') +ylabel(ax(1),'Wavelength (nm)') +ax(2) = subplot(3,1,2,'parent',f); hold(ax(2),'on'); +xlabel(ax(2),'Time (s)') +ylabel(ax(2),'Voltage (V)') +ax(3) = subplot(3,1,3,'parent',f); hold(ax(3),'on'); +xlabel(ax(3),'Time (s)') +ylabel(ax(3),'Power (uW)') +if isempty(wm) + title(ax(1),'No Active Channels'); + return +end + + + % Wavelength plots +plot(ax(1),NaN,NaN,'tag','wm','UserData',struct('wm',wm)); + % DAC Voltage plots +plot(ax(2),NaN,NaN,'tag','wm'); + % Power Plots +plot(ax(3),NaN,NaN,'tag','wm'); + +hold(ax(1),'off'); +hold(ax(2),'off'); +hold(ax(3),'off'); +leg = cellfun(@num2str,{1},'UniformOutput',false); +leg = legend(ax(1),leg,'orientation','horizontal','location','northoutside'); + +t = timer('name',mfilename,'executionmode','fixedspacing','period',dt,'StopFcn',@cleanup,... + 'timerfcn',@update,'userdata',struct('ax',ax,'wm',wm,'t0',tic,'leg',leg),'busymode','drop'); +set(f,'UserData',struct('timer',t)); +start(f.UserData.timer); +end + +function update(hObj,~) +UserData = hObj.UserData; +ax = UserData.ax; +pWav = findall(ax(1),'tag','wm'); +pVol = findall(ax(2),'tag','wm'); +pPow = findall(ax(3),'tag','wm'); +if all(isvalid(ax)) && length(pWav)==length(pVol) + for i = 1:length(pWav) + t = toc(UserData.t0); + wm = pWav(i).UserData.wm; + vlt = NaN; + try %#ok + vlt = wm.getDeviationVoltage; + end + pVol(i).YData(end+1) = vlt; + pVol(i).XData(end+1) = t; + if wm.getPIDstatus && wm.getDeviationChannel + UserData.leg.String{i} = sprintf('\\color{red}%i',i); + else + UserData.leg.String{i} = sprintf('%i',i); + end + wl = NaN; + try %#ok + wl = wm.getWavelength; + end + pWav(i).YData(end+1) = wl; + pWav(i).XData(end+1) = t; + + pow = NaN; + try %#ok + pow = wm.getPower(); + end + pPow(i).YData(end+1) = pow; + pPow(i).XData(end+1) = t; + end + drawnow limitrate; +else % Clean up + stop(hObj) +end +end + +function cleanup(hObj,~) +UserData = hObj.UserData; +delete(UserData.wm) +delete(hObj) +end