PageRenderTime 24ms CodeModel.GetById 0ms RepoModel.GetById 0ms app.codeStats 0ms

/ATF2/FlightSim/trustedApps/bpmcal/nmCavCal.m

http://atf2flightsim.googlecode.com/
MATLAB | 638 lines | 502 code | 17 blank | 119 comment | 108 complexity | edafce2c78d1da646639f8d7f13c6731 MD5 | raw file
Possible License(s): BSD-2-Clause, LGPL-2.0, IPL-1.0, BSD-3-Clause
  1. function [stat varargout] = nmCavCal(cmd,varargin)
  2. % NMCAVCAL
  3. % Non-Mover-Based Cavity BPM calibration routines
  4. % Also for use with EXT striplines
  5. %
  6. % stat = nmCavCal('init')
  7. % Run initialisation steps
  8. % - Setup default bump calibration parameters
  9. % - Define correctors used for bpm bumps
  10. % - Setup multiknobs for bumps
  11. % NOTE: This step runs automatically on first call, call again if optics
  12. % change requiring a recalculation of multiknobs
  13. %
  14. % [stat pars] = nmCavCal('GetPars')
  15. % Readout internal parameters (see pars list below)
  16. %
  17. % stat = nmCavCal('SetPars',newpars)
  18. % Fields of newpars get written to internal pars structure
  19. %
  20. % stat = nmCavCal('SetBump',bpmname,axis,val,nocal)
  21. % Set bump value at desired BPM
  22. % bpmname = BEAMLINE name of BPM
  23. % axis = 'x' or 'y' or 'xp' or 'yp'
  24. % val = bump size / m
  25. % If calibration data exists for this bump, the expected extra kick is
  26. % removed from the last corrector unless nocal=true specified
  27. %
  28. % stat = nmCavCal('RestoreBumps',bpmname,axis)
  29. % Restore bumps on all BPMs to their zero value if 1 argument provided
  30. % If bpmname and axis provided, just do that one bump
  31. %
  32. % stat = nmCavCal('ResetBumps',bpmname,axis)
  33. % Set zero value of all bumps to represent current location if 1 argument
  34. % provided
  35. % If bpmname and axis provided, just do that one bump
  36. %
  37. % stat = nmCavCal('CalibBump',bpmname,axis)
  38. % Perform a calibration of requested BPM bump
  39. % bpmname = BEAMLINE name
  40. % axis = 'x' or 'y' 'xp' or 'yp'
  41. % Calibrates over pars.bumpcalib.nsteps
  42. % of moving over a range +/- pars.bumpcalib.size (m)
  43. % using average of pars.bumpcalib.nbpm BPM readings to calculate orbit at
  44. % each step
  45. %
  46. % stat = nmCavCal('CalibBpm',bpmname,axis)
  47. % Perform calibration of bpm bpmname (axis 'x' or 'y' or 'xp' or 'yp')
  48. % pars.bpmcalib.usesvd use SVD technique to remove main jitter modes
  49. % Calibrates over pars.bpmcalib.nsteps
  50. % of moving over a range +/- pars.bpmcalib.size (m)
  51. % using average of pars.bpmcalib.nbpm BPM readings to calculate orbit at
  52. % each step
  53. %
  54. % =====================================
  55. % Internal Data Parameters
  56. % =====================================
  57. % pars.
  58. % bpmnames : name list of all cavity non-mover bpms
  59. % bpmele(.x | .y) : corresponding BEAMLINE elements
  60. % cornames(.x | .y) : corrector names used for bumps
  61. % corele(.x | .y) : corresponding BEAMLINE elements
  62. % bumpknob.(.x | .y){ibpm} : Lucretia MultiKnob for bpms
  63. % bumpcalib.
  64. % nbpm : num bpm readings per calibration step
  65. % size : range (+/-) of calibration sweep
  66. % nstep : number of steps across range
  67. % (bpmname).(x | .y) : calibration slope for bpm
  68. % (bpmname).(xdate | .ydate): last calibration date
  69. % :: calibration slope gives expected extra kick from last
  70. % in bump corrector given by multiplying calibration
  71. % value with bump size
  72. % bpmcalib.
  73. % nbpm : num bpm readings per calibration step
  74. % size : range (+/-) of calibration sweep
  75. % nstep : number of steps across range
  76. % nrepeat : number of times to iterate calibration sweep
  77. % usesvd : use SVD to remove jitter modes
  78. global INSTR PS BEAMLINE GIRDER %#ok<NUSED>
  79. persistent pars
  80. stat{1}=1;
  81. % Load any previously saved data
  82. if isempty(pars)
  83. if exist('userData/nmCavCal.mat','file')
  84. cavdata=load('userData/nmCavCal');
  85. else
  86. cavdata.pars=[];
  87. end
  88. if ~isfield(cavdata,'pars')
  89. cavdata.pars=[];
  90. end
  91. pars=init(cavdata.pars);
  92. if strcmpi(cmd,'init')
  93. return
  94. end
  95. end
  96. switch lower(cmd)
  97. case 'init'
  98. pars=init(pars);
  99. case 'getpars'
  100. varargout{1}=pars;
  101. case 'setpars'
  102. if nargin==2
  103. newpars=varargin{1};
  104. if isstruct(newpars)
  105. pf=fields(newpars);
  106. for ipar=1:length(pf)
  107. if isstruct(newpars.(pf{ipar}))
  108. pf2=fields(newpars.(pf{ipar}));
  109. for ipar2=1:length(pf2)
  110. pars.(pf{ipar}).(pf2{ipar2})=newpars.(pf{ipar}).(pf2{ipar2});
  111. end
  112. else
  113. pars.(pf{ipar})=newpars.(pf{ipar});
  114. end
  115. end
  116. end
  117. end
  118. case 'calibbpm'
  119. if nargin~=3 || ~ischar(varargin{1}) || (~isequal(varargin{2},'x') && ~isequal(varargin{2},'y') && ~isequal(varargin{2},'xp') && ~isequal(varargin{2},'yp'))
  120. error('Incorrect input arguments for CalibBpm')
  121. end
  122. stat=calibrateBPM(pars,varargin{1},varargin{2});
  123. if stat{1}~=1; error(stat{2}); end;
  124. case 'restorebumps'
  125. if nargin==3
  126. wk=find(ismember(pars.bpmnames,varargin{1}), 1);
  127. pars.bumpknob.(varargin{2}){wk}.Value=0;
  128. for ichan=1:length(pars.bumpcomp.(varargin{2}){wk})
  129. evalc([pars.bumpknob.(varargin{2}){wk}.Channel(ichan).Parameter,'=',num2str(pars.bumpcomp.(varargin{2}){wk}(ichan),10)]);
  130. if ~isempty(strfind(pars.bumpknob.(varargin{2}){wk}.Channel(ichan).Parameter,'GIRDER'))
  131. MoverTrim(pars.bumpknob.(varargin{2}){wk}.Channel(ichan).Unit,1);
  132. else
  133. PSTrim(pars.bumpknob.(varargin{2}){wk}.Channel(ichan).Unit,1);
  134. end
  135. end
  136. else
  137. disp('Putting all correctors to their zero-bump positions...')
  138. for ibpm=1:length(pars.bpmnames)
  139. for ichan=1:length(pars.bumpcomp.x{ibpm})
  140. evalc([pars.bumpknob.x{ibpm}.Channel(ichan).Parameter,'=',num2str(pars.bumpcomp.x{ibpm}(ichan),10)]);
  141. evalc([pars.bumpknob.xp{ibpm}.Channel(ichan).Parameter,'=',num2str(pars.bumpcomp.xp{ibpm}(ichan),10)]);
  142. if ~isempty(strfind(pars.bumpknob.x{ibpm}.Channel(ichan).Parameter,'GIRDER'))
  143. MoverTrim(pars.bumpknob.x{ibpm}.Channel(ichan).Unit,1);
  144. else
  145. PSTrim(pars.bumpknob.x{ibpm}.Channel(ichan).Unit,1);
  146. end
  147. if ~isempty(strfind(pars.bumpknob.xp{ibpm}.Channel(ichan).Parameter,'GIRDER'))
  148. MoverTrim(pars.bumpknob.xp{ibpm}.Channel(ichan).Unit,1);
  149. else
  150. PSTrim(pars.bumpknob.xp{ibpm}.Channel(ichan).Unit,1);
  151. end
  152. end
  153. for ichan=1:length(pars.bumpcomp.y{ibpm})
  154. evalc([pars.bumpknob.y{ibpm}.Channel(ichan).Parameter,'=',num2str(pars.bumpcomp.y{ibpm}(ichan),10)]);
  155. if ~isempty(strfind(pars.bumpknob.y{ibpm}.Channel(ichan).Parameter,'GIRDER'))
  156. MoverTrim(pars.bumpknob.y{ibpm}.Channel(ichan).Unit,1);
  157. else
  158. PSTrim(pars.bumpknob.y{ibpm}.Channel(ichan).Unit,1);
  159. end
  160. if ~isempty(strfind(pars.bumpknob.yp{ibpm}.Channel(ichan).Parameter,'GIRDER'))
  161. MoverTrim(pars.bumpknob.yp{ibpm}.Channel(ichan).Unit,1);
  162. else
  163. PSTrim(pars.bumpknob.yp{ibpm}.Channel(ichan).Unit,1);
  164. end
  165. end
  166. end
  167. end
  168. case 'resetbumps'
  169. if nargin==3
  170. wk=find(ismember(pars.bpmnames,varargin{1}), 1);
  171. pars.bumpknob.(varargin{2}){wk}.Value=0;
  172. for ichan=1:length(pars.bumpcomp.(varargin{2}){wk})
  173. evalc(['pars.bumpcomp.(varargin{2}){wk}(ichan)=',pars.bumpknob.(varargin{2}){wk}.Channel(ichan).Parameter]);
  174. end
  175. else
  176. disp('Setting all bump references to zero here...')
  177. for ibpm=1:length(pars.bpmnames)
  178. pars.bumpknob.x{ibpm}.Value=0;
  179. pars.bumpknob.y{ibpm}.Value=0;
  180. pars.bumpknob.xp{ibpm}.Value=0;
  181. pars.bumpknob.yp{ibpm}.Value=0;
  182. end
  183. pars=init(pars);
  184. end
  185. case 'setbump'
  186. if nargin<4 || ~ischar(varargin{1}) || (~isequal(varargin{2},'x') && ~isequal(varargin{2},'y') && ...
  187. ~isequal(varargin{2},'xp') && ~isequal(varargin{2},'yp')) || ~isnumeric(varargin{3}) || length(varargin{3})~=1
  188. error('Incorrect arguments to ''set''')
  189. end
  190. wk=find(ismember(pars.bpmnames,varargin{1}), 1);
  191. if isempty(wk)
  192. stat{1}=-1;
  193. stat{2}='Unknown knob for this BPM name';
  194. return
  195. end
  196. stat = SetMultiKnob( 'pars.bumpknob.(varargin{2}){wk}', varargin{3}, 1);
  197. % fine tune bump if calibration exists
  198. if isfield(pars.bumpcalib,varargin{1}) && isfield(pars.bumpcalib.(varargin{1}),varargin{2}) &&...
  199. (nargin<5 || ~varargin{4})
  200. extrakick=varargin{3}*pars.bumpcalib.(varargin{1}).(varargin{2});
  201. PS(BEAMLINE{pars.corele.(varargin{2}){wk}(end)}.PS).SetPt=...
  202. PS(BEAMLINE{pars.corele.(varargin{2}){wk}(end)}.PS).SetPt-extrakick;
  203. stat=PSTrim(BEAMLINE{pars.corele.(varargin{2}){wk}(end)}.PS,1);
  204. if stat{1}~=1; return; end;
  205. end
  206. case 'calibbump'
  207. if nargin~=3 || ~ischar(varargin{1}) || (~isequal(varargin{2},'x') && ~isequal(varargin{2},'y') && ~isequal(varargin{2},'xp') && ~isequal(varargin{2},'yp'))
  208. error('Incorrect arguments to ''set''')
  209. end
  210. wk=find(ismember(pars.bpmnames,varargin{1}), 1);
  211. if isempty(wk)
  212. stat{1}=-1;
  213. stat{2}='Unknown knob for this BPM name';
  214. return
  215. end
  216. [stat data] = FlBpmToolFn('GetData');
  217. if stat{1}~=1; return; end;
  218. % Get good bpms to use
  219. goodbpm=data.global.hw & data.global.use; % & data.global.cal;
  220. % Get bpms downstream of last corrector used in this bump
  221. bpmele=cellfun(@(x) x.Index,INSTR);
  222. bpmind=bpmele>pars.corele.(varargin{2}){wk}(end);
  223. bpms=goodbpm&bpmind;
  224. if ~any(bpms)
  225. errordlg('No good BPMs to use!','Bump Calibration Error')
  226. return
  227. end
  228. % Reset bump value
  229. pars.bumpknob.(varargin{2}){wk}.Value=0;
  230. % Get reference orbit
  231. disp('Calibrating Bump...')
  232. disp('Getting reference orbit...')
  233. FlHwUpdate('wait',pars.bumpcalib.nbpm);
  234. [stat bpmdata]=FlHwUpdate('bpmave',pars.bumpcalib.nbpm); if stat{1}~=1; return; end;
  235. bpmref.x=bpmdata{1}(1,:); bpmref.y=bpmdata{1}(2,:);
  236. nanbpm=isnan(bpmref.x) | isnan(bpmref.y);
  237. % Ramp bump and collect orbit data
  238. ibump=0;
  239. bumps=linspace(-pars.bumpcalib.size,pars.bumpcalib.size,pars.bumpcalib.nstep);
  240. for bumpsize=bumps
  241. ibump=ibump+1;
  242. dispstr=sprintf('Bump calibration position %d of %d...',ibump,length(bumps));
  243. disp(dispstr)
  244. stat = SetMultiKnob( 'pars.bumpknob.(varargin{2}){wk}', bumpsize, 1); if stat{1}~=1; return; end;
  245. pause(3); % wait for correctors to settle
  246. FlHwUpdate('wait',pars.bumpcalib.nbpm);
  247. [stat bpmdata]=FlHwUpdate('bpmave',pars.bumpcalib.nbpm); if stat{1}~=1; return; end;
  248. nanbpm=nanbpm | isnan(bpmdata{1}(1,:)) | isnan(bpmdata{1}(2,:));
  249. [stat X Xerr] = bpmfit(bpmele(bpms&~nanbpm),bpmdata{1}(1,bpms&~nanbpm)-bpmref.x(bpms&~nanbpm),...
  250. bpmdata{1}(2,bpms&~nanbpm)-bpmref.y(bpms&~nanbpm),...
  251. pars.corele.(varargin{2}){wk}(end),bpmdata{1}(4,bpms&~nanbpm),bpmdata{1}(5,bpms&~nanbpm));
  252. if stat{1}~=1; return; end;
  253. extrakick.x(ibump)=X(2); extrakick.y(ibump)=X(4);
  254. extrakickerr.x(ibump)=Xerr(2); extrakickerr.y(ibump)=Xerr(4);
  255. end
  256. % Return bump and get calibration slope
  257. disp('Returning bump to initial setting and calculating calibration...')
  258. stat = SetMultiKnob( 'pars.bumpknob.(varargin{2}){wk}', 0, 1); if stat{1}~=1; return; end;
  259. figure
  260. if varargin{2}=='x'
  261. [q,dq]=plot_polyfit(bumps,extrakick.x,extrakickerr.x,1);
  262. if queryCalib(1)
  263. pars.bumpcalib.(varargin{1}).x=q(end); pars.bumpcalib.(varargin{1}).xerr=dq(end);
  264. pars.bumpcalib.(varargin{1}).xdate=now;
  265. end
  266. else
  267. [q,dq]=plot_polyfit(bumps,extrakick.y,extrakickerr.y,1);
  268. if queryCalib(2)
  269. pars.bumpcalib.(varargin{1}).y=q(end); pars.bumpcalib.(varargin{1}).yerr=dq(end);
  270. pars.bumpcalib.(varargin{1}).ydate=now;
  271. end
  272. end
  273. drawnow('expose')
  274. otherwise
  275. error('Unknown command')
  276. end
  277. % store pars data
  278. save userData/nmCavCal pars
  279. %% Internal Functions
  280. function resp = queryCalib(dim)
  281. global FL PS GIRDER %#ok<NUSED>
  282. resp=true;
  283. if ~isfield(FL,'Gui') || ~isfield(FL.Gui,'bpmcal') || ~isfield(FL.Gui.bpmcal,'figure1') || ...
  284. ~ishandle(FL.Gui.bpmcal.figure1)
  285. return
  286. end
  287. if dim==1
  288. set(FL.Gui.bpmcal.pushbutton8,'Visible','on')
  289. set(FL.Gui.bpmcal.pushbutton11,'Visible','on')
  290. else
  291. set(FL.Gui.bpmcal.pushbutton9,'Visible','on')
  292. set(FL.Gui.bpmcal.pushbutton10,'Visible','on')
  293. end
  294. uiwait(FL.Gui.bpmcal.figure1)
  295. if ~ishandle(FL.Gui.bpmcal.figure1) || ...
  296. (get(FL.Gui.bpmcal.figure1,'CurrentObject')~=FL.Gui.bpmcal.pushbutton8 && ...
  297. get(FL.Gui.bpmcal.figure1,'CurrentObject')~=FL.Gui.bpmcal.pushbutton9)
  298. resp=false;
  299. end
  300. if ishandle(FL.Gui.bpmcal.figure1) && dim==1
  301. set(FL.Gui.bpmcal.pushbutton8,'Visible','off')
  302. set(FL.Gui.bpmcal.pushbutton11,'Visible','off')
  303. elseif ishandle(FL.Gui.bpmcal.figure1)
  304. set(FL.Gui.bpmcal.pushbutton9,'Visible','off')
  305. set(FL.Gui.bpmcal.pushbutton10,'Visible','off')
  306. end
  307. function pars = init(pars)
  308. global BEAMLINE PS GIRDER INSTR %#ok<NUSED>
  309. disp('Initialising data structures and generating bumps...')
  310. % List of BPMs
  311. pars.bpmnames={'MQD10X' 'MQF11X' 'MQD12X' 'MQD16X' 'MQF17X' 'MQD18X' 'MQF19X' 'MQD20X' 'MQF21X' 'MQM16FF'... % 'FONTP1' 'FONTP2' 'FONTP3' ...
  312. 'MQF1X' 'MQD2X' 'MQF3X' 'MQF4X' 'MQD5X' 'MQF6X' 'MQF7X' 'MQD8X' 'MQF9X' 'MQF13X' 'MQD14X' 'MQF15X' 'LW1X'};
  313. pars.bpmele=zeros(size(pars.bpmnames));
  314. try
  315. for ibpm=1:length(pars.bpmnames)
  316. pars.bpmele(ibpm)=findcells(BEAMLINE,'Name',pars.bpmnames{ibpm});
  317. pars.bpminst(ibpm)=findcells(INSTR,'Index',pars.bpmele(ibpm));
  318. pars.bpmtype{ibpm}=INSTR{pars.bpminst(ibpm)}.Type;
  319. end
  320. catch ME
  321. error(ME.message)
  322. end
  323. % Corrector list for orbit bumps
  324. % Use 3-corrector bumps where there are not 2 downstream correctors to bpm
  325. % for now, later use a downstream quad on a mover as 4th corrector
  326. pars.cornames.x={};
  327. pars.cornames.y={};
  328. pars.cornames.x{1}={'ZH3X' 'ZH4X' 'ZH5X' 'ZH6X'};
  329. pars.cornames.x{2}={'ZH4X' 'ZH5X' 'ZH6X' 'ZH7X'};
  330. pars.cornames.x{3}={'ZH4X' 'ZH5X' 'ZH6X' 'ZH7X'};
  331. pars.cornames.x{4}={'ZH6X' 'ZH7X' 'ZH8X' 'ZH9X'};
  332. pars.cornames.x{5}={'ZH7X' 'ZH8X' 'ZH9X' 'ZH10X'};
  333. pars.cornames.x{6}={'ZH7X' 'ZH8X' 'ZH9X' 'ZH10X'};
  334. pars.cornames.x{7}={'ZH8X' 'ZH9X' 'ZH10X' 'ZH1FF'};
  335. pars.cornames.x{8}={'ZH8X' 'ZH9X' 'ZH10X' 'ZH1FF'};
  336. pars.cornames.x{9}={'ZH9X' 'ZH10X' 'ZH1FF' 'QD10BFF'};
  337. pars.cornames.x{10}={'ZH9X' 'ZH10X' 'ZH1FF' 'QD10BFF'};
  338. % pars.cornames.x{11}={'ZH4X' 'ZH5X' 'ZH6X'};
  339. % pars.cornames.x{12}={'ZH5X' 'ZH6X' 'ZH7X'};
  340. % pars.cornames.x{13}={'ZH5X' 'ZH6X' 'ZH7X'};
  341. pars.cornames.x{11}={'ZH101RX' 'ZX1X' 'ZH1X'};
  342. pars.cornames.x{12}={'ZX1X' 'ZH1X' 'ZH2X'};
  343. pars.cornames.x{13}={'ZX1X' 'ZH1X' 'ZH2X'};
  344. pars.cornames.x{14}={'ZH1X' 'ZH2X' 'ZH3X'};
  345. pars.cornames.x{15}={'ZH2X' 'ZH3X' 'ZH4X'};
  346. pars.cornames.x{16}={'ZH2X' 'ZH3X' 'ZH4X'};
  347. pars.cornames.x{17}={'ZH2X' 'ZH3X' 'ZH4X'};
  348. pars.cornames.x{18}={'ZH3X' 'ZH4X' 'ZH5X'};
  349. pars.cornames.x{19}={'ZH3X' 'ZH4X' 'ZH5X'};
  350. pars.cornames.x{20}={'ZH6X' 'ZH7X' 'ZH8X'};
  351. pars.cornames.x{21}={'ZH6X' 'ZH7X' 'ZH8X'};
  352. pars.cornames.x{22}={'ZH7X' 'ZH8X' 'ZH9X'};
  353. pars.cornames.x{23}={'ZH8X' 'ZH9X' 'ZH10X' 'ZH1FF'};
  354. pars.cornames.y{1}={'ZV6X' 'ZV7X' 'ZV8X' 'ZV9X'};
  355. pars.cornames.y{2}={'ZV6X' 'ZV7X' 'ZV8X' 'ZV9X'};
  356. pars.cornames.y{3}={'ZV7X' 'ZV8X' 'ZV9X' 'ZV10X'};
  357. pars.cornames.y{4}={'ZV8X' 'ZV9X' 'ZV10X' 'ZV11X'};
  358. pars.cornames.y{5}={'ZV8X' 'ZV9X' 'ZV10X' 'ZV11X'};
  359. pars.cornames.y{6}={'ZV9X' 'ZV10X' 'ZV11X' 'ZV1FF'};
  360. pars.cornames.y{7}={'ZV9X' 'ZV10X' 'ZV11X' 'ZV1FF'};
  361. pars.cornames.y{8}={'ZV10X' 'ZV11X' 'ZV1FF' 'QD10BFF'};
  362. pars.cornames.y{9}={'ZV10X' 'ZV11X' 'ZV1FF' 'QD10BFF'};
  363. pars.cornames.y{10}={'ZV10X' 'ZV11X' 'ZV1FF' 'QD10BFF'};
  364. % pars.cornames.y{11}={'ZV7X' 'ZV8X' 'ZV9X'};
  365. % pars.cornames.y{12}={'ZV8X' 'ZV9X' 'ZV10X'};
  366. % pars.cornames.y{13}={'ZV8X' 'ZV9X' 'ZV10X'};
  367. pars.cornames.y{11}={'ZV1X' 'ZV2X' 'ZV3X'};
  368. pars.cornames.y{12}={'ZV2X' 'ZV3X' 'ZV4X'};
  369. pars.cornames.y{13}={'ZV2X' 'ZV3X' 'ZV4X'};
  370. pars.cornames.y{14}={'ZV3X' 'ZV4X' 'ZV5X'};
  371. pars.cornames.y{15}={'ZV3X' 'ZV4X' 'ZV5X'};
  372. pars.cornames.y{16}={'ZV5X' 'ZV6X' 'ZV7X'};
  373. pars.cornames.y{17}={'ZV5X' 'ZV6X' 'ZV7X'};
  374. pars.cornames.y{18}={'ZV5X' 'ZV6X' 'ZV7X'};
  375. pars.cornames.y{19}={'ZV6X' 'ZV7X' 'ZV8X'};
  376. pars.cornames.y{20}={'ZV8X' 'ZV9X' 'ZV10X'};
  377. pars.cornames.y{21}={'ZV8X' 'ZV9X' 'ZV10X'};
  378. pars.cornames.y{22}={'ZV8X' 'ZV9X' 'ZV10X'};
  379. pars.cornames.y{23}={'ZV10X' 'ZV11X' 'ZV1FF' 'QD10BFF'};
  380. pars.corele.x=cell(1,length(pars.bpmnames));
  381. pars.corele.y=cell(1,length(pars.bpmnames));
  382. for icor=1:length(pars.bpmnames)
  383. for icor2=1:length(pars.cornames.x{icor})
  384. ele=findcells(BEAMLINE,'Name',pars.cornames.x{icor}{icor2});
  385. pars.corele.x{icor}(icor2)=ele(1);
  386. end
  387. for icor2=1:length(pars.cornames.y{icor})
  388. ele=findcells(BEAMLINE,'Name',pars.cornames.y{icor}{icor2});
  389. pars.corele.y{icor}(icor2)=ele(1);
  390. end
  391. end
  392. % Form bump multiknobs
  393. useApp('bumpgui');
  394. pars.bumpknob.x={}; pars.bumpknob.y={};
  395. for ibpm=1:length(pars.bpmnames)
  396. pars.bumpknob.x{ibpm} = bumpgui(1,length(pars.corele.x{ibpm}),pars.bpmele(ibpm),pars.corele.x{ibpm});
  397. pars.bumpknob.y{ibpm} = bumpgui(3,length(pars.corele.y{ibpm}),pars.bpmele(ibpm),pars.corele.y{ibpm});
  398. pars.bumpknob.xp{ibpm} = bumpgui(2,length(pars.corele.x{ibpm}),pars.bpmele(ibpm),pars.corele.x{ibpm});
  399. pars.bumpknob.yp{ibpm} = bumpgui(4,length(pars.corele.y{ibpm}),pars.bpmele(ibpm),pars.corele.y{ibpm});
  400. end
  401. % Bump Calibration parameters
  402. if ~isfield(pars,'bumpcalib')
  403. pars.bumpcalib.nbpm=10; % nuber of bpm readings to average
  404. pars.bumpcalib.nstep=5; % number of bump steps to use
  405. pars.bumpcalib.size=500e-6; % +/- size of bump to calibrate over
  406. end
  407. % BPM Calibration parameters
  408. if ~isfield(pars,'bpmcalib')
  409. pars.bpmcalib.nbpm=20; % nuber of bpm readings to average
  410. pars.bpmcalib.nstep=5; % number of bump steps to use
  411. pars.bpmcalib.size=1e-3; % +/- size of bump to calibrate over
  412. pars.bpmcalib.usesvd=false;
  413. pars.bpmcalib.nrepeat=1; % number of times to repeat the calibration process
  414. end
  415. % Initial bump component settings
  416. for ibpm=1:length(pars.bpmnames)
  417. for ichan=1:length(pars.bumpknob.x{ibpm}.Channel)
  418. evalc(['pars.bumpcomp.x{ibpm}(ichan)=',pars.bumpknob.x{ibpm}.Channel(ichan).Parameter]);
  419. evalc(['pars.bumpcomp.xp{ibpm}(ichan)=',pars.bumpknob.xp{ibpm}.Channel(ichan).Parameter]);
  420. end
  421. for ichan=1:length(pars.bumpknob.y{ibpm}.Channel)
  422. evalc(['pars.bumpcomp.y{ibpm}(ichan)=',pars.bumpknob.y{ibpm}.Channel(ichan).Parameter]);
  423. evalc(['pars.bumpcomp.yp{ibpm}(ichan)=',pars.bumpknob.yp{ibpm}.Channel(ichan).Parameter]);
  424. end
  425. end
  426. function stat=calibrateBPM(pars,bpmname,axis)
  427. global INSTR FL
  428. stat{1}=1;
  429. % BPM index
  430. wk=find(ismember(pars.bpmnames,bpmname), 1);
  431. if isempty(wk)
  432. stat{1}=-1;
  433. stat{2}='BPM name not found in internal structure';
  434. return
  435. end
  436. bpminst=findcells(INSTR,'Index',pars.bpmele(wk));
  437. % Is there a GUI out there?
  438. if isfield(FL,'Gui') && isfield(FL.Gui,'bpmcal_calFig') && ishandle(FL.Gui.bpmcal_calFig.figure1)
  439. isgui=true;
  440. else
  441. isgui=false;
  442. end
  443. % Take the data and calculate calibration slopes
  444. for irep=1:pars.bpmcalib.nrepeat
  445. bsteps=linspace(-pars.bpmcalib.size,pars.bpmcalib.size,pars.bpmcalib.nstep);
  446. bpmcal(1).xraw=[]; bpmcal(1).yraw=[];
  447. bpmcal(1).xraw_bump=[]; bpmcal(1).yraw_bump=[];
  448. for istep=randperm(length(bsteps))
  449. bpmcal(irep).bpmname=bpmname;
  450. % Set bump
  451. bpmcal(irep).bump(istep)=bsteps(istep);
  452. stat = SetMultiKnob( 'pars.bumpknob.(axis){wk}', bsteps(istep), 1); if stat{1}~=1; return; end;
  453. % Take averaged orbit data or collect data for svd
  454. % wait for pulses
  455. [stat,pnum]=FlHwUpdate('getpulsenum'); if stat{1}~=1; return; end;
  456. pnum_end=pnum+pars.bpmcalib.nbpm;
  457. while pnum<pnum_end
  458. % Is stop button pushed?
  459. if isgui
  460. resp=exitTest('commit',bpmcal);
  461. if resp
  462. return
  463. elseif isfield(FL.Gui,'bpmcal_calFig') && ishandle(FL.Gui.bpmcal_calFig.figure1)
  464. set(FL.Gui.bpmcal_calFig.text2,'String',sprintf('Taking New Data (%d of %d)...',pnum,pnum_end))
  465. drawnow('expose')
  466. end
  467. end
  468. pause(FL.Period_floodland);
  469. FlHwUpdate;
  470. [stat,pnum]=FlHwUpdate('getpulsenum'); if stat{1}~=1; return; end;
  471. end
  472. if pars.bpmcalib.usesvd
  473. [stat bpmdata]=FlHwUpdate('bpmave',pars.bpmcalib.nbpm); if stat{1}~=1; return; end;
  474. xbad=bpmdata{1}(7,:) & bpmdata{1}(9,:);
  475. ybad=bpmdata{1}(8,:) & bpmdata{1}(9,:);
  476. [stat bpmdata]=FlHwUpdate('readbuffer',pars.bpmcalib.nbpm); if stat{1}~=1; return; end;
  477. % store EXT and FFS BPMs
  478. ibpm1=findcells(INSTR,'Index',findcells(BEAMLINE,'Name','MQF1X'));
  479. bpmcal(1).ibpm=bpminst-ibpm1+1;
  480. if ((strcmp(axis,'x') || strcmp(axis,'xp')) && all(xbad)) || ((strcmp(axis,'y') || strcmp(axis,'yp')) && all(ybad))
  481. continue
  482. else
  483. if any(~xbad)
  484. bpmcal(1).xraw(end+1:end+sum(~xbad),:)=bpmdata(~xbad,ibpm1*3-1:3:end);
  485. bpmcal(1).xraw_bump(end+1:end+sum(~xbad),1)=bsteps(istep);
  486. end
  487. if any(~ybad)
  488. bpmcal(1).yraw(end+1:end+sum(~ybad),:)=bpmdata(~ybad,ibpm1*3:3:end);
  489. bpmcal(1).yraw_bump(end+1:end+sum(~ybad),1)=bsteps(istep);
  490. end
  491. end
  492. bpmcal=calib_calc('svd',bpmcal,axis);
  493. else
  494. npulse=pars.bpmcalib.nbpm;
  495. [stat bpmdata]=FlHwUpdate('bpmave',npulse); if stat{1}~=1; return; end;
  496. nbad=sum(bpmdata{1}(7,:) & bpmdata{1}(8,:) & bpmdata{1}(9,:));
  497. % if any bad pulses, take more
  498. while npulse-nbad < pars.bpmcalib.nbpm
  499. npulse=pars.bpmcalib.nbpm+nbad;
  500. % wait for new pulses
  501. [stat,pnum]=FlHwUpdate('getpulsenum'); if stat{1}~=1; return; end;
  502. pnum_end=pnum+nbad;
  503. while pnum<pnum_end
  504. % Is stop button pushed?
  505. if isgui
  506. resp=exitTest('commit',bpmcal);
  507. if resp
  508. return
  509. elseif isfield(FL.Gui,'bpmcal_calFig') && ishandle(FL.Gui.bpmcal_calFig.figure1)
  510. set(FL.Gui.bpmcal_calFig.text2,'String',sprintf('Taking New Data (%d of %d)...',npulse-nbad,pars.bpmcalib.nbpm))
  511. drawnow('expose')
  512. end
  513. end
  514. pause(FL.Period_floodland);
  515. FlHwUpdate;
  516. [stat,pnum]=FlHwUpdate('getpulsenum'); if stat{1}~=1; return; end;
  517. end
  518. [stat bpmdata]=FlHwUpdate('bpmave',npulse); if stat{1}~=1; return; end;
  519. nbad=sum(bpmdata{1}(7,:) & bpmdata{1}(8,:) & bpmdata{1}(9,:));
  520. end
  521. bpmcal(irep).x(istep)=bpmdata{1}(1,bpminst); bpmcal(irep).y(istep)=bpmdata{1}(2,bpminst);
  522. bpmcal(irep).xerr(istep)=bpmdata{1}(4,bpminst); bpmcal(irep).yerr(istep)=bpmdata{1}(4,bpminst);
  523. bpmcal=calib_calc('standard',bpmcal,axis);
  524. end
  525. rawdata(istep).bpmdata=bpmdata; %#ok<NASGU>
  526. end
  527. end
  528. % Commit results
  529. if isgui
  530. calib_calc('commit',bpmcal);
  531. end
  532. % Save data
  533. if ~exist('userData/nmCavCal','dir')
  534. mkdir('userData','nmCavCal');
  535. save(sprintf('userData/nmCavCal/bpmcalibdata-%s.mat',datestr(now,FL.tstamp)),'rawdata','bpmcal');
  536. end
  537. save userData
  538. function bpmcal=calib_calc(cmd,bpmcal,axis)
  539. global FL
  540. if strcmp(cmd,'commit')
  541. if isfield(bpmcal,'xscale') && strcmp(questdlg(sprintf('%s\nx slope = %g +/- %g','Commit New Calibration?',bpmcal(1).xscale,bpmcal(1).xscale_err),'Commit Cal'),'Yes')
  542. lcaPut([bpmcal(1).bpmname,':SCALE_X'],lcaGet([bpmcal(1).bpmname,':SCALE_X'])*bpmcal(1).xscale);
  543. elseif ~isfield(bpmcal,'xscale') && strcmp(questdlg(sprintf('%s\ny slope = %g +/- %g','Commit New Calibration?',bpmcal(1).yscale,bpmcal(1).yscale_err),'Commit Cal'),'Yes')
  544. lcaPut([bpmcal(1).bpmname,':SCALE_Y'],lcaGet([bpmcal(1).bpmname,':SCALE_Y'])*bpmcal(1).yscale);
  545. end
  546. else
  547. if isfield(FL,'Gui') && isfield(FL.Gui,'bpmcal_calFig') && ishandle(FL.Gui.bpmcal_calFig.figure1)
  548. figure(FL.Gui.bpmcal_calFig.figure1)
  549. else
  550. FL.Gui.bpmcal_calFig.figure1=bpmcal_calFig;
  551. end
  552. if strcmp(cmd,'standard')
  553. % average over multiple scans
  554. for iscan=1:length(bpmcal)
  555. % change order by x-axis
  556. [Y Ix]=sort(bpmcal(iscan).bump);
  557. bpmval(iscan,1:length(bpmcal(iscan).(axis)))=bpmcal(iscan).(axis)(Ix);
  558. bpmval_err(iscal,1:length(bpmcal(iscan).(axis)))=bpmcal(iscan).([axis 'err'])(Ix);
  559. xval(iscan,:)=Y;
  560. end
  561. % Only average over completed scan steps
  562. for iscan=1:length(xval(1,:))
  563. bpmval(iscan)=mean(bpmval(ismember(xval,xval(1,iscan))&bpmval~=0));
  564. bpmval_err(iscan)=sqrt(sum(bpmval_err(ismember(xval,xval(1,iscan))&bpmval~=0).^2))/sum(ismember(xval,xval(1,iscan)));
  565. end
  566. elseif strcmp(cmd,'svd')
  567. % Unpack raw data and compute SVD
  568. xval=bpmcal(1).xraw_bump;
  569. yval=bpmcal(1).([axis 'raw']);
  570. [xval xind]=sort(xval);
  571. yval=yval(xind,:);
  572. [Ux,Sx,Vtx]=svd(yval);
  573. Vx=Vtx';
  574. % Correlate modes with orbit bump
  575. for imode=1:10
  576. [r,p] = corrcoef(xval,yval*Vx(imode,:)');
  577. if ~isempty(find(p<0.1, 1))
  578. rcor(imode)=r(1,2);
  579. else
  580. rcor(imode)=0;
  581. end % if any significant correlations
  582. end % for imode
  583. if ~any(rcor)
  584. return
  585. end % if no correlations found, return
  586. % throw away all modes but that most correlated to bump, reform bpm
  587. % data and pull out BPM of interest
  588. Sx_new=zeros(size(Sx));
  589. [~, rind]=max(rcor);
  590. Sx_new(rind,rind)=Sx(rind,rind);
  591. bpmval_all=Ux*Sx_new*Vx;
  592. uxval=unique(xval);
  593. for ix=1:length(uxval)
  594. bpmval(ix)=mean(bpmval_all(ismember(xval,uxval(ix)),bpmcal(1).ibpm));
  595. bpmval_err(ix)=std(bpmval_all(ismember(xval,uxval(ix)),bpmcal(1).ibpm));
  596. end
  597. xval=uxval;
  598. end
  599. plot_polyfit(FL.Gui.bpmcal_calFig.axes1); % set polyfit plot axes
  600. [q dq]=plot_polyfit(xval.*1e3,bpmval.*1e3,bpmval_err.*1e3,2,[axis ' Bump Size'],'BPM reading','mm','mm');
  601. bpmcal(1).([axis 'scale'])=q/1e3; bpmcal(1).([axis 'scale_err'])=dq/1e3;
  602. if isfield(FL,'Gui') && isfield(FL.Gui,'bpmcal_calFig') && ishandle(FL.Gui.bpmcal_calFig.figure1)
  603. set(FL.Gui.bpmcal_calFig.text1,'String',sprintf('%.3f +/- %.3f (mm)',q,dq))
  604. drawnow('expose')
  605. end
  606. end
  607. function resp=exitTest(bpmcal)
  608. global FL
  609. resp=false;
  610. if isfield(FL,'Gui') && isfield(FL.Gui,'bpmcal_calFig') && ishandle(FL.Gui.bpmcal_calFig.figure1)
  611. if get(FL.Gui.bpmcal_calFig,'togglebutton2')
  612. resp=true;
  613. guiCloseFn('bpmcal_calFig',FL.Gui.bpmcal_calFig);
  614. elseif get(FL.Gui.bpmcal_calFig,'togglebutton1')
  615. resp=true;
  616. calib_calc('commit',bpmcal);
  617. guiCloseFn('bpmcal_calFig',FL.Gui.bpmcal_calFig);
  618. end
  619. else
  620. resp=true;
  621. end