PageRenderTime 63ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 1ms

/aoutil.i

http://github.com/frigaut/yao
Swig | 2096 lines | 1604 code | 237 blank | 255 comment | 0 complexity | 988f086c7ede4b81a2d9e86fb00c9681 MD5 | raw file
Possible License(s): GPL-2.0

Large files files are truncated, but you can click here to view the full file

  1. /* AOUTIL.I
  2. *
  3. * A collection of utility routines to go with yao.i
  4. *
  5. * This file is part of the yao package, an adaptive optics simulation tool.
  6. *
  7. * Copyright (c) 2002-2017, Francois Rigaut
  8. *
  9. * This program is free software; you can redistribute it and/or modify it
  10. * under the terms of the GNU General Public License as published by the
  11. * Free Software Foundation; either version 2 of the License, or (at your
  12. * option) any later version.
  13. *
  14. * This program is distributed in the hope that it will be useful, but
  15. * WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  17. * General Public License for more details (to receive a copy of the GNU
  18. * General Public License, write to the Free Software Foundation, Inc., 675
  19. * Mass Ave, Cambridge, MA 02139, USA).
  20. *
  21. */
  22. func yao_tricks(void)
  23. {
  24. write,format="%-78s\n","FLAGS:";
  25. txt = "dispImImav: Image (PSF) display: (0) inst. PSF (1): avg PSF (2) phases";
  26. grow,txt,"display_phase_not_wfs: (bool) Display phases instead of WFS images";
  27. write,format="%-78s\n",txt;
  28. write,"";
  29. write,format="%-78s\n","HOOKS: (see code in yao.i, go() function)";
  30. txt = "go_user_func: called at the start of go(), before entering the loop is go has been called with all=1";
  31. grow,txt,"go2_user_func: called at the start of go(), after entering the loop is go has been called with all=1";
  32. grow,txt,"user_loop_err: called after calculation of err from measurements";
  33. grow,txt,"user_loop_command: called after calculation of command vector";
  34. grow,txt,"";
  35. write,format="%-78s\n",txt;
  36. write,"";
  37. }
  38. func init_image_display(void)
  39. {
  40. if (target._ntarget == 1) {
  41. // if only one target, display correct plate scale for image display
  42. *target.dispzoom = [(*target.lambda)(0)*1e-6/4.848e-6/
  43. tel.diam*sim.pupildiam/sim._size/2*sim._size];
  44. // last 2 terms to accomodate internal dispzoom scaling
  45. }
  46. txpos = *target.xposition;
  47. typos = *target.yposition;
  48. disp2d,im,txpos,typos,1,zoom=*target.dispzoom,init=1;
  49. for (j=1;j<=nwfs;j++) {
  50. // plg,wfs(j).gspos(2),wfs(j).gspos(1),marker='\2',
  51. // type="none",marks=1,color="red";
  52. if (wfs(j).gsalt==0) plp,wfs(j).gspos(2),wfs(j).gspos(1),symbol=8,color="fg",width=2,size=0.6;
  53. else if (wfs(j).gsalt>50000) plp,wfs(j).gspos(2),wfs(j).gspos(1),symbol=8,color="orange",width=2,size=0.6;
  54. else plp,wfs(j).gspos(2),wfs(j).gspos(1),symbol=8,color="green",width=2,size=0.6;
  55. }
  56. myxytitles,"","arcsec",[0.005,0.01],height=12;
  57. plmargin;
  58. if (wfs_display_mode=="spatial") {
  59. disp2d,wfs._dispimage,wfs.pupoffset(1,),wfs.pupoffset(2,),2,\
  60. zoom=wfs.dispzoom,init=1;
  61. } else {
  62. disp2d,wfs._dispimage,wfs.gspos(1,),wfs.gspos(2,),2,zoom=wfs.dispzoom,\
  63. init=1;
  64. }
  65. return 0;
  66. }
  67. func create_yao_window(dpi)
  68. /* DOCUMENT create_yao_window(dpi)
  69. Open or re-open (e.g. after a fork() ) the main and only yao graphical
  70. window.
  71. SEE ALSO:
  72. */
  73. {
  74. // dpi is in fact already stored in extern (sigh)
  75. if (!dpi) dpi=default_dpi;
  76. if (window_exists(2)) winkill,2;
  77. if (yao_pyk_parent_id) {
  78. // there's a GUI, re-parent within GUI drawingarea:
  79. yao_win_init,yao_pyk_parent_id;
  80. } else {
  81. // there's no GUI, re-open normal graphical window:width=635,height=650
  82. // check if there's one existing of the right size. I'm fed up to see
  83. // yao window pop up everywhere ;-(
  84. need2open = 0;
  85. // is there a window 0?
  86. if (!window_exists(0)) {
  87. need2open=1;
  88. // write,"There is no window 0, need to create";
  89. } else {
  90. // is the size allright?
  91. wg = long(window_geometry(0));
  92. // write,wg(-1:0);
  93. // write,long([635*(dpi/75.),650*(dpi/75.)]);
  94. if (nallof(wg(-1:0)==long([635*(dpi/75.),650*(dpi/75.)]))) {
  95. need2open=1;
  96. } else {
  97. // is there 5 plsys, in which case it's probably a yao window?
  98. window,0;
  99. get_style,l,sys;
  100. if (numberof(sys)!=5) need2open=1;
  101. }
  102. }
  103. if (need2open) {
  104. if (window_exists(0)) winkill,0;
  105. window,0,style="yao.gs",dpi=dpi,width=long(635*(dpi/75.)), \
  106. height=long(650*(dpi/75.)),wait=1;
  107. if ( (xft!=[]) && (xft()) ) {
  108. get_style, landscape, systems, legends, clegends;
  109. systems.ticks.vert.textStyle.height(4)*=1.5;
  110. systems.ticks.horiz.textStyle.height(4)*=1.5;
  111. set_style, landscape, systems, legends, clegends;
  112. }
  113. }
  114. }
  115. plsys,1; limits; limits,square=1;
  116. plsys,2; limits; limits,square=1;
  117. }
  118. //----------------------------------------------------
  119. func xyxy2xxyy(void)
  120. {
  121. offset=0;
  122. for (n=1;n<=nwfs;n++) {
  123. tmp = indgen(wfs(n)._nsub)*2;
  124. tmp = _(tmp,tmp+1);
  125. grow,reordered,tmp+offset;
  126. offset+=wfs(n)._nmes;
  127. }
  128. return reordered;
  129. }
  130. func xxyy2xyxy(void)
  131. {
  132. offset=0;
  133. for (n=1;n<=nwfs;n++) {
  134. grow,reordered,transpose(reform(indgen(wfs(n)._nmes),
  135. [2,wfs(n)._nsub,2]))(*)+offset;
  136. offset+=wfs(n)._nmes;
  137. }
  138. return reordered;
  139. }
  140. //----------------------------------------------------
  141. func get_turb_phase_initCheckOverflow(void)
  142. /* DOCUMENT func get_turb_phase_initCheckOverflow
  143. This routine has the sole purpose of checking the possible "overflow"
  144. of the Y index in the future calls to get_turb_phase. In any of the
  145. Y index at which the interpolation is to be done is larger than the
  146. phase screen(s) Y dimension, then an error is flagged.
  147. SEE ALSO: get_turb_phase_init, get_turb_phase
  148. */
  149. {
  150. extern screendim;
  151. dimy = screendim(2);
  152. if (max(wfsyposcub(max,,)+yposvec(max,)(,-)) > dimy) {
  153. write,"\nSome of the phase screens are too small (Y dimension) "+
  154. "for the specified system.";
  155. write,format="The following WFS/screens will cause a "+
  156. "index overflow (current Ydim is %d):\n",dimy;
  157. maxind = wfsyposcub(max,,)+yposvec(max,)(,-);
  158. w2 = where2(maxind > dimy);
  159. for (i=1;i<=dimsof(w2)(3);i++) {
  160. write,format="WFS#%d, screen#%d, Ymax=%f\n",w2(2,i),w2(1,i),
  161. maxind(w2(1,i),w2(2,i));
  162. }
  163. write,"To remedy this situation, you can either:";
  164. write," - use larger phase screens (Y axis) using 'create_phase_screens'";
  165. write," - Modify the extremum Y offsets of your WFSs";
  166. write," - Lower the altitude of the offending atmospheric layer";
  167. exit;
  168. }
  169. if (max(gsyposcub(max,,)+yposvec(max,)(,-)) > dimy) {
  170. write,"\nSome of the phase screens are too small (Y dimension) "+
  171. "for the specified system.";
  172. write,format="The following Perf Star/screens will cause a "+
  173. "index overflow (current Ydim is %d):\n",dimy;
  174. maxind = gsyposcub(max,,)+yposvec(max,)(,-);
  175. w2 = where2(maxind > dimy);
  176. for (i=1;i<=dimsof(w2)(3);i++) {
  177. write,format="Perf.Star#%d, screen#%d, Ymax=%f\n",w2(2,i),w2(1,i),
  178. maxind(w2(1,i),w2(2,i));
  179. }
  180. write,"To remedy this situation, you can either:";
  181. write," - use larger phase screens (Y axis) using 'create_phase_screens'";
  182. write," - Modify the Y position of your Perf. Star";
  183. write," - Lower the altitude of the offending atmospheric layer";
  184. exit;
  185. }
  186. }
  187. //----------------------------------------------------
  188. func graphic_config(subsystemnum,dmnum)
  189. /* DOCUMENT func configGraphic(void)
  190. Plots a graphical representation of the system config,
  191. per subsystem and per level (altitude)
  192. subsystemnum and dmnum optional. If not set all subsystems
  193. and dms are displayed
  194. SEE ALSO:
  195. */
  196. {
  197. t = span(0.,2*pi,100);
  198. col = ["red","blue","green","cyan","magenta","yellow"];
  199. markers = ['1','2','3','4','5','6','7','8','9'];
  200. markers = char(indgen(36)+48); // 1,2,...A,B...
  201. maxcol = numberof(col);
  202. plsys,1;
  203. limits,square=1;
  204. psize = tel.diam/sim.pupildiam;
  205. for (nss=1;nss<=max(dm.subsystem);nss++) {
  206. if ( (subsystemnum != [] ) && (subsystemnum != nss) ) continue;
  207. fma;
  208. // one level by one level
  209. for (i=1;i<=ndm;i++) {
  210. if ( (dmnum != [] ) && (dmnum != i) ) continue;
  211. if ( dm(i).subsystem != nss ) continue;
  212. if ( dm(i).type == "aniso" ) continue;
  213. for (j=1;j<=nwfs;j++) {
  214. if ( wfs(j).subsystem != nss ) continue;
  215. // overplot subaperture position and size for first wfs in this subsystem
  216. first_wfs_plotted = 0;
  217. if ( (first_wfs_plotted==0) && (dm(i).alt==0.) && (dm(i).type!="tiptilt") ) {
  218. for (jj=1;jj<=wfs(j)._nsub4disp;jj++) {
  219. if ((*wfs(j)._validsubs)(jj)==0) continue;
  220. x1 = (*wfs(j)._istart)(jj);
  221. y1 = (*wfs(j)._jstart)(jj);
  222. subsize = sim.pupildiam/wfs(j).shnxsub;
  223. if (wfs(j).npixpersub) subsize = wfs(j).npixpersub;
  224. l1 = subsize;
  225. // convert pixels in meters
  226. x1 = (x1-sim._cent)*tel.diam/sim.pupildiam;
  227. y1 = (y1-sim._cent)*tel.diam/sim.pupildiam;
  228. l1 = l1*tel.diam/sim.pupildiam;
  229. plg,_(y1,y1,y1+l1,y1+l1,y1),_(x1,x1+l1,x1+l1,x1,x1),color=[50,50,50];
  230. }
  231. first_wfs_plotted = 1;
  232. }
  233. }
  234. // radius of outmost actuator in meters:
  235. // rad = (dm(i).nxact-1)/2.*dm(i).pitch*psize;
  236. // plots circle containing all actuators
  237. // plg,rad*sin(t),rad*cos(t),type=2;
  238. // radius of outmost object beam in meter:
  239. tmp = sqrt((*target.xposition)^2.+(*target.yposition)^2.);
  240. maxobjrad = max(tmp);
  241. rad = max(tmp)*4.848e-6*dm(i).alt+tel.diam/2.;
  242. // plots circle containing all rays from objects:
  243. plg,rad*sin(t),rad*cos(t),type=3;
  244. if ( dm(i).type == "stackarray" ) {
  245. // build array containing (x,y) location of valid/controlled actuators:
  246. loc = ([*(dm(i)._x),*(dm(i)._y)]-sim._cent)*psize;
  247. // and plot:
  248. plg,loc(,2),loc(,1),type=0,marker=markers(i),color=col(i%maxcol);
  249. if (numberof(*dm(i)._ex) != 0) {
  250. // build array containing (x,y) location of extrapolated actuators:
  251. loc = ([*(dm(i)._ex),*(dm(i)._ey)]-sim._cent)*psize;
  252. // and plot:
  253. plg,loc(,2),loc(,1),type=0,marker=markers(i);
  254. }
  255. }
  256. // loop on sensor:
  257. for (j=1;j<=nwfs;j++) {
  258. if ( wfs(j).subsystem != nss ) continue;
  259. // computes scaling factor due to limited altitude:
  260. if (wfs(j)._gsalt != 0) {
  261. fact = (wfs(j)._gsalt-dm(i).alt)/wfs(j)._gsalt;
  262. } else {
  263. fact=1;
  264. }
  265. offsets = wfs(j).gspos*4.848e-6*dm(i).alt;
  266. rad=tel.diam/2.*fact;
  267. // plotting beam shape for this wfs at this dm altitude:
  268. plg,offsets(2)+rad*sin(t),offsets(1)+rad*cos(t),color=col(i%maxcol),width=3;
  269. }
  270. myxytitles,"Beam outline [m]","Beam outline [m]",[0.0,0.0];
  271. mypltitle,swrite(format="Patches of guide star beams on DM#%d, Subsystem %d",i,nss);
  272. comment = swrite(format="Configuration actuator/beams in a plane at altitude %.0f\n"+
  273. "Solid lines: Outline of this subsystem GS beams\n"+
  274. "Dotted line: Circle including outermost ray to specified science \n"+
  275. " objects (at %.1f arcsec off-center)\n"+
  276. "Numbers for stackarrays actuators: colored:controlled, BW: extrapolated\n"+
  277. "# of active actuators= %d, # of extrapolated actuators=%d",
  278. dm(i).alt,maxobjrad,dm(i)._nact,dm(i)._enact);
  279. plt,comment,0.06,0.22,tosys=0,justify="LT";
  280. plt,sim.name,0.01,0.227,tosys=0;
  281. limits; lim=limits(); limits,lim(1)*1.1,lim(2)*1.1,lim(3)*1.1,lim(4)*1.1;
  282. hcp;
  283. if (sim.debug >=1) typeReturn;
  284. fma;
  285. }
  286. if ( subsystemnum != [] ) continue;
  287. // all levels
  288. for (i=1;i<=ndm;i++) {
  289. if ( dm(i).subsystem != nss ) continue;
  290. if ( dm(i).type == "aniso" ) continue;
  291. // radius of outmost actuator in meters:
  292. rad = (dm(i).nxact-1)/2.*dm(i).pitch*psize;
  293. // plots circle containing all actuators
  294. plg,rad*sin(t),rad*cos(t),type=i;
  295. if ( dm(i).type == "stackarray" ) {
  296. // build array containing (x,y) location of valid actuators:
  297. loc = ([*(dm(i)._x),*(dm(i)._y)]-sim._cent)*psize;
  298. // and plot:
  299. plg,loc(,2),loc(,1),type=0,marker=markers(i),color=col(i%maxcol);
  300. }
  301. // loop on sensor:
  302. for (j=1;j<=nwfs;j++) {
  303. if ( wfs(j).subsystem != nss ) continue;
  304. // computes scaling factor due to limited altitude:
  305. if (wfs(j)._gsalt != 0) {
  306. fact = (wfs(j)._gsalt-dm(i).alt)/wfs(j)._gsalt;
  307. } else {
  308. fact=1;
  309. }
  310. offsets = wfs(j).gspos*4.848e-6*dm(i).alt;
  311. rad=tel.diam/2.*fact;
  312. // plotting beam shape for this wfs at this dm altitude:
  313. plg,offsets(2)+rad*sin(t),offsets(1)+rad*cos(t),color=col(i%maxcol),width=3;
  314. }
  315. }
  316. myxytitles,"Beam outline [m]","Beam outline [m]",[0.0,0.0];
  317. mypltitle,swrite(format="Patches of guide star beams on DMs, Subsystem %d",nss);
  318. plt,sim.name,0.01,0.227,tosys=0;
  319. limits; lim=limits(); limits,lim(1)*1.1,lim(2)*1.1,lim(3)*1.1,lim(4)*1.1;
  320. hcp;
  321. if (sim.debug >=1) typeReturn;
  322. }
  323. }
  324. //----------------------------------------------------
  325. func check_parameters(void)
  326. /* DOCUMENT func check_parameters(void)
  327. Check the parameters in yao parfile
  328. - set defaults
  329. - value valid?
  330. - compatibility with other parameters
  331. SEE ALSO:
  332. */
  333. {
  334. extern sim,atm,wfs,dm,mat,tel,target,gs,loop;
  335. if (sim.verbose) write,format="Checking parameters ... %s\n","";
  336. //==================================================================
  337. // BASIC EXISTENCE AND CONSISTENCY CHECKINGS AND DEFAULT ASSIGNMENTS
  338. //==================================================================
  339. extern nwfs,ndm,ntarget,noptics;
  340. nwfs = numberof(wfs);
  341. ndm = numberof(dm);
  342. ntarget = numberof(*target.lambda);
  343. noptics = numberof(opt);
  344. // SIM STRUCTURE
  345. if (sim.pupildiam == 0) {exit,"sim.pupildiam has not been set";}
  346. if ( ((sim.svipc>>2)&1) && (!((sim.svipc>>0)&1)) ) {
  347. write,"If you have (sim.svipc>>2)&1, you should have (sim.svipc>>0)&1";
  348. write,"Setting sim.svipc first bit to 1"
  349. sim.svipc++;
  350. pause,2000;
  351. }
  352. if (sim.svipc_wfs_nfork>nwfs) {
  353. write,"sim.svipc_wfs_nfork > nwfs, setting sim.svipc_wfs_nfork = nwfs"
  354. sim.svipc_wfs_nfork = nwfs;
  355. pause,2000;
  356. }
  357. // ATM STRUCTURE
  358. if ((*atm.screen) == []) {exit,"atm.screen has not been set";}
  359. if (typeof(*atm.screen) != "string") {exit,"*atm.screen is not a string";}
  360. // Check that all phase screens are different. If the same file is repeated, this makes the turbulence much worse than what is specified by r0!
  361. ftmp = *atm.screen;
  362. for (i=1;i<=numberof(ftmp);i++){
  363. if (sum(ftmp == (ftmp)(i)) > 1){
  364. write, "atm.screen must not have repeated values";
  365. write, "doing so changes the statistics of the atmospheric turbulence";
  366. error,"exiting";
  367. }
  368. }
  369. for (i=1;i<=numberof(ftmp);i++) {
  370. if (!open(ftmp(i),"r",1)) { // file does not exist
  371. msg = swrite(format="Phase screen %s not found!\\n"+
  372. "You need to generate phase screens for yao.\\n"+
  373. "Go to \"Phase Screen -> Create phase Screen\"\\n"+
  374. "If you already have phase screens, you can modify\\n"+
  375. "the path in the parfile atm.screen definition",ftmp(i));
  376. if (_pyk_proc) pyk,"set_cursor_busy(0)";
  377. pyk_warning,msg;
  378. msg = swrite(format="\nWARNING: %s not found!\nEdit the par file and change the "+
  379. "path,\nand/or run \"Phase Screen -> Create phase Screen\"\n",ftmp(i));
  380. write,msg;
  381. break;
  382. }
  383. }
  384. if ((*atm.layerfrac) == []) {exit,"atm.layerfrac has not been set";}
  385. if ((*atm.layerspeed) == []) {exit,"atm.layerspeed has not been set";}
  386. if ((*atm.layeralt) == []) {exit,"atm.layeralt has not been set";}
  387. if ((*atm.winddir) == []) {exit,"atm.winddir has not been set";}
  388. if (nallof(_(numberof(*atm.screen),numberof(*atm.layerfrac),numberof(*atm.layerspeed), \
  389. numberof(*atm.layeralt)) == numberof(*atm.winddir))) {
  390. exit,"Some elements within atm.screen, layerfrac, layerspeed, layeralt \n"+\
  391. "or winddir do not have the same number of elements.";
  392. }
  393. // WFS STRUCTURE
  394. for (ns=1;ns<=nwfs;ns++) {
  395. if (wfs(ns).type == string()) {
  396. exit,swrite(format="wfs(%d).type has not been set",ns);
  397. }
  398. // if (wfs(ns).subsystem == 0) {wfs(ns).subsystem = 1;}
  399. if (wfs(ns).lambda == 0) {
  400. exit,swrite(format="wfs(%d).lambda has not been set",ns);
  401. }
  402. //~ if (wfs(ns).dispzoom == 0) {wfs(ns).dispzoom = 1;}
  403. if ((wfs(ns).type == "curvature") && ((*wfs(ns).nsubperring) == [])) {
  404. write,format="wfs(%d).nsubperring has not been set.\n",ns;
  405. write,"Valid values are, for example:";
  406. exit,"wfs(n).nsubperring = &([6,12,18]);";
  407. }
  408. if ((wfs(ns).type == "curvature") && (wfs(ns).l == 0)) {
  409. exit,swrite(format="wfs(%d).l has not been set",ns);
  410. }
  411. if ((wfs(ns).type == "curvature") && (wfs(ns).gsdepth > 0)) {
  412. exit,swrite(format="wfs(%d): gsdepth not implemented for CWFS, sorry",ns);
  413. }
  414. if ((wfs(ns).type == "curvature") && (wfs(ns).rayleighflag == 1)) {
  415. exit,swrite(format="wfs(%d): Rayleigh not implemented for CWFS, sorry",ns);
  416. }
  417. if ((wfs(ns).gsalt == 0) && (wfs(ns).rayleighflag == 1)) {
  418. write,swrite(format="wfs(%d): gsalt = 0 and Rayleigh flag is set !",ns);
  419. exit,"Rayleigh is not computed for NGS sensors, sorry.";
  420. }
  421. if ((wfs(ns).gsalt > 0) && (wfs(ns).laserpower == 0)) {
  422. exit,swrite(format="wfs(%d): this is a LGS and you haven't set wfs.laserpower",ns);
  423. }
  424. if ((wfs(ns).type == "curvature") && (wfs(ns).fieldstopdiam == 0)) {
  425. wfs(ns).fieldstopdiam = 1.f;
  426. }
  427. if ((wfs(ns).type == "hartmann") && (wfs(ns).shmethod == 0)) {
  428. exit,swrite(format="wfs(%d).shmethod has not been set",ns);
  429. }
  430. if ((wfs(ns).type == "hartmann") && (wfs(ns).shnxsub == 0)) {
  431. exit,swrite(format="wfs(%d).shnxsub has not been set",ns);
  432. }
  433. if ((wfs(ns).type == "hartmann") && (wfs(ns).shmethod == 2)) {
  434. if ((wfs(ns).type == "hartmann") && (wfs(ns).pixsize == 0)) {
  435. exit,swrite(format="wfs(%d).pixsize has not been set",ns);
  436. }
  437. if ((wfs(ns).type == "hartmann") && (wfs(ns).npixels == 0)) {
  438. exit,swrite(format="wfs(%d).npixels has not been set",ns);
  439. }
  440. if ((wfs(ns).type == "hartmann") && (wfs(ns).shthmethod == 0)) {
  441. wfs(ns).shthmethod = 1;
  442. }
  443. if ((wfs(ns).type == "hartmann") && (wfs(ns).shthmethod == 3) &&
  444. ((wfs(ns).shthreshold > (wfs(ns).npixels)^2) || (wfs(ns).shthreshold <= 0))) {
  445. exit,swrite(format="Wrong wfs(%d).shthreshold value for wfs(%d).shthmethod = %d",ns,ns,wfs(ns).shthmethod);
  446. }
  447. }
  448. if ((wfs(ns).type == "hartmann") && (wfs(ns).shmethod == 2)) {
  449. if ((strlen(wfs(ns).fsname)>0) && (strlen(wfs(ns).fstop)>0)) {
  450. write,format="You have set both wfs(%d).fsname and wfs(%d).fstop\n",ns,ns;
  451. write,format="I will ignore wfs(%d).fstop and use wfs(%d).fsname !\n",ns,ns;
  452. }
  453. if ((strlen(wfs(ns).fsname)==0) && (strlen(wfs(ns).fstop)==0)) {
  454. write,format="No field stop defined for wfs %d. Setting to 'square'\n",ns;
  455. wfs(ns).fstop = "square";
  456. }
  457. if (wfs(ns).fssize==0) {
  458. write,format="wfs(%d).fssize has not been set, will be forced to subap FoV\n",ns;
  459. }
  460. }
  461. if ((wfs(ns).type != "hartmann")&&(wfs(ns).LLT_uplink_turb)) {
  462. write,format="WARNING, wfs#%d: LLT_uplink_turb only implemented for SHWFS, ignoring.\n",ns;
  463. }
  464. if (wfs(ns).LLT_uplink_turb) {
  465. if ((wfs(ns).LLTr0==0)) {
  466. write,format="WARNING, wfs(%d).LLTr0 undefined, Using atm-defined r0\n",ns;
  467. wfs(ns).LLTr0 = tel.diam/atm.dr0at05mic * (wfs(ns).lambda/0.5)^1.2;
  468. }
  469. if ((wfs(ns).LLTdiam==0)) \
  470. error,swrite(format="wfs#%d: LLT_uplink_turb set but LLTdiam not defined",ns);
  471. if ((wfs(ns).LLT1overe2diam==0)) \
  472. error,swrite(format="wfs#%d: LLT_uplink_turb set but LLT1overe2diam not defined",ns);
  473. }
  474. if (wfs(ns).nintegcycles == 0) {wfs(ns).nintegcycles = 1;}
  475. if (wfs(ns).fracIllum == 0) {wfs(ns).fracIllum = 0.5;}
  476. if (wfs(ns).optthroughput == 0) {wfs(ns).optthroughput = 1.0;}
  477. if (wfs(ns).svipc>1) {
  478. if (wfs(ns).type!="hartmann") {
  479. write,format="wfs(%d).svipc >1 only for SHWFS, will have no effect\n",ns;
  480. wfs(ns).svipc = 0;
  481. } else if (wfs(ns).shnxsub < 2) {
  482. write,format="wfs(%d).svipc >1 but SH 1x1 sub. Disabling ||.\n",ns;
  483. wfs(ns).svipc = 0;
  484. }
  485. }
  486. if (wfs(ns).type == "pyramid") {
  487. if ((wfs(ns).pyr_mod_loc!="after") && (wfs(ns).pyr_mod_loc!="before")) {
  488. write,format="wfs(%d).pyr_mod_loc is not set, setting to \"after\"\n",ns;
  489. }
  490. }
  491. // DMs in this WFS path. Default is for all DM to be in path:
  492. wfs(ns)._dmnotinpath = &array(0n,ndm);
  493. if (wfs(ns).dmnotinpath) {
  494. if (anyof(*wfs(ns).dmnotinpath>ndm)||anyof(*wfs(ns).dmnotinpath<1)) \
  495. error,swrite(format="Issue with WFS#%d dmnotinpath definition (indices)",ns);
  496. (*wfs(ns)._dmnotinpath)(int(*wfs(ns).dmnotinpath)) = 1n;
  497. }
  498. wfs(ns).ron = float(wfs(ns).ron);
  499. if ((wfs(ns).type == "hartmann") && (wfs(ns).shmethod == 1) && (wfs(ns).noise == 1) && (wfs(ns).ron > 0.)){
  500. write, format = "WARNING: wfs(%d).shmethod = 1 and wfs(%d).noise = 1\n",ns,ns;
  501. write, format = "This feature produces a measurement error in wfs(%d) of wfs(%d).ron arcsec\n",ns,ns;
  502. }
  503. if (!wfs(ns).lgs_prof_amp) wfs(ns).lgs_prof_amp = &float([0.]);
  504. if (!wfs(ns).lgs_prof_alt) wfs(ns).lgs_prof_alt = &float([0.]);
  505. if (numberof(*wfs(ns).lgs_prof_amp)!=numberof(*wfs(ns).lgs_prof_alt)) {
  506. error,"numberof(*wfs(ns).lgs_prof_amp)!=numberof(*wfs(ns).lgs_prof_alt)";
  507. }
  508. if (wfs(ns).minzer == 0) {
  509. wfs(ns).minzer = 1;
  510. }
  511. }
  512. // DM STRUCTURE
  513. for (nm=1;nm<=ndm;nm++) {
  514. if (dm(nm).type == string()) {
  515. exit,swrite(format="dm(%d).type has not been set",nm);
  516. }
  517. // disk harmonic type string has changed. for compatibility
  518. // with old API:
  519. if (dm(nm).type == "diskharmonic") dm(nm).type = "dh";
  520. if (dm(nm).subsystem == 0) {dm(nm).subsystem = 1;}
  521. if (dm(nm).iffile == string()) {dm(nm).iffile = "";}
  522. if (dm(nm).ecmatfile == string()) {dm(nm).ecmatfile = "";}
  523. if (dm(nm).push4imat == 0) {dm(nm).push4imat = 20;}
  524. if (dm(nm).filterpiston == 0) {dm(nm).filterpiston = 1;}
  525. if (dm(nm).gain == 0) {write,format=" WARNING: dm(%d).gain set to 0\n",nm;}
  526. if (dm(nm).unitpervolt == 0) {
  527. write,format=" WARNING: dm(%d).unitpervolt set to 0\n",nm;
  528. // below: this is stupid. but I don't dare to change it now (2011mar16)
  529. if ( (dm(nm).type == "tiptilt") || (dm(nm).type == "zernike")) {
  530. dm(nm).unitpervolt = 0.0005;
  531. } else if ( (dm(nm).type == "bimorph") || (dm(nm).type == "dh")) {
  532. dm(nm).unitpervolt = 1.0;
  533. } else {
  534. dm(nm).unitpervolt = 0.01;
  535. }
  536. write,format=" WARNING: dm(%d).unitpervolt overwritten to %f\n",nm,dm(nm).unitpervolt;
  537. }
  538. if ( (dm(nm).type == "bimorph") && ((*dm(nm).nelperring) == []) ) {
  539. write,format="dm(%d).nelperring has not been set.\n",nm;
  540. write,"Valid values are, for example:";
  541. exit,"dm(n).nelperring = &([6,12,18]);";
  542. }
  543. if ( (dm(nm).type == "stackarray") && (dm(nm).nxact == 0) ) {
  544. exit,swrite(format="dm(%d).nxact has not been set",nm);
  545. }
  546. if ( (dm(nm).type == "stackarray") && (dm(nm).pitch == 0) ) {
  547. exit,swrite(format="dm(%d).pitch has not been set",nm);
  548. }
  549. if (dm(nm).type=="stackarray") {
  550. if ((dm(nm).coupling<0.0) || (dm(nm).coupling>0.8)) {
  551. write,format="Invalid value for dm(%d).coupling -> %f\n",nm,dm(nm).coupling;
  552. exit,"Valid values from 0 to 0.80";
  553. }
  554. }
  555. if ( (dm(nm).type == "zernike") && (dm(nm).nzer == 0) ) {
  556. exit,swrite(format="dm(%d).nzer has not been set",nm);
  557. }
  558. if ( (dm(nm).type == "dh") && (dm(nm).ndh == 0) ) {
  559. exit,swrite(format="dm(%d).ndh has not been set",nm);
  560. }
  561. if (dm(nm).minzer == 0) {
  562. dm(nm).minzer = 1;
  563. }
  564. if ( (dm(nm).filtertilt) && (noneof(dm(nm).type == ["zernike","stackarray"]) )) {write, "WARNING: filtertilt only defined for zernike and stackarray DMs";}
  565. if ( (dm(nm).type == "kl") && (dm(nm).nkl == 0) ) {
  566. exit,swrite(format="dm(%d).nkl has not been set",nm);
  567. }
  568. if (dm(nm).irexp==1) {
  569. if (dm(nm).irfact == 0.) {
  570. dm(nm).irfact = 1.;
  571. write,format="dm(%d).irfact set to %f\n",nm,dm(nm).irfact;
  572. }
  573. }
  574. // check that laplacian only applies to stackarrays
  575. if ((dm(nm).regtype == "laplacian") && (dm(nm).type != "stackarray")){
  576. exit, "regtype can only be set to laplacian for stackarray DMs";
  577. }
  578. // set the regparam for stack arrays to something other than 0
  579. if ((dm(nm).type == "stackarray") && (dm(nm).regparam == 0)){
  580. write,"Setting regparam to 1e-5";
  581. dm(nm).regparam = 1e-5;
  582. }
  583. // check that any virtual DMs have a lower DM number than a DM that uses it
  584. if (*dm(nm).dmfit_which != []){
  585. if (max(*dm(nm).dmfit_which) > nm){
  586. write,format="Virtual DMs (%d) must have a lower numbering than the DM (%d) that fits to them\n",max(*dm(nm).dmfit_which),nm;
  587. exit, "Exiting";
  588. }
  589. }
  590. if ((dm(nm).hyst < 0.)+(dm(nm).hyst > 0.25)){exit,"DM hysteresis must be between 0 and 0.25";}
  591. if (dm(nm).hyst > 0.){write,"WARNING: DM hysteresis implementation assumes that voltages are of the order of 0.1 to 10. If not, do not use hysteresis.";}
  592. }
  593. nmaniso = where(dm.type == "aniso");
  594. if (numberof(nmaniso) > 1){
  595. exit, "The number of DMs with dm.type == aniso must be 0 or 1";
  596. }
  597. if (numberof(nmaniso) == 1){
  598. nmaniso = nmaniso(1);
  599. // use dm.dmfit_which to specify which DMs these modes are projected onto
  600. dmfit = dm(nmaniso).anisodmfit;
  601. if (numberof(dmfit) != 2){
  602. // the user needs to specify two DMs at different altitudes
  603. exit,"Exactly 2 DMs must be specified in anisodmfit for aniso DM";
  604. }
  605. }
  606. // MAT STRUCTURE
  607. if (mat.method == string()) {mat.method = "svd";};
  608. if (mat.method == "svd"){
  609. if ((*mat.condition) == []) {exit,"mat.condition has not been set";}
  610. if (numberof(*mat.condition) != max(_(wfs.subsystem,dm.subsystem)) ) {
  611. exit,"dimension of *mat.condition is not equal to the number of subsystems";
  612. }
  613. }
  614. if (mat.file == string()) {mat.file = "";}
  615. if (mat.sparse_MR == long()){mat.sparse_MR = 10000;}
  616. if (mat.sparse_MN == long()){mat.sparse_MN = 200000;}
  617. if (mat.sparse_thresh == float()){mat.sparse_thresh = 1e-8;}
  618. if (mat.sparse_pcgtol == float()){mat.sparse_pcgtol = 1e-6;}
  619. if (mat.fit_subsamp == long()){mat.fit_subsamp = 1;}
  620. if (mat.fit_minval == float()){mat.fit_minval = 1e-2;}
  621. // TEL STRUCTURE
  622. if (tel.diam == 0) exit,"tel.diam has not been set";
  623. // TARGET STRUCTURE
  624. if ((*target.lambda) == []) exit,"target.lambda has not been set";
  625. target.lambda = &(float(*target.lambda)); // *target.lambda must be floats
  626. if ((*target.xposition) == []) exit,"target.xposition has not been set";
  627. if ((*target.yposition) == []) exit,"target.yposition has not been set";
  628. if ((*target.dispzoom) == []) {
  629. target.dispzoom = &(array(1.,numberof(*target.xposition)));
  630. }
  631. if (nallof(_(numberof(*target.xposition), \
  632. numberof(*target.yposition)) == numberof(*target.dispzoom) )) {
  633. exit,"Some elements within target.xposition, yposition, dispzoom "+\
  634. "do not have the same number of elements.";
  635. }
  636. // GS STRUCTURE
  637. if (gs.zenithangle > 0 && anyof(wfs.gsalt > 0)){write,"WARNING: The return from the LGS is assumed to vary as cos(gs.zenithangle).";}
  638. if (anyof(wfs.gsalt != 0) && (gs.lgsreturnperwatt == 0)) {
  639. gs.lgsreturnperwatt = 22.;
  640. write,format="gs.lgsreturnperwatt set to %f\n",gs.lgsreturnperwatt;
  641. }
  642. if (anyof((wfs.gsalt == 0)*(wfs.zeropoint == 0)) && (gs.zeropoint == 0)) {
  643. exit,"You have some NGS and gs.zeropoint has not been set";
  644. }
  645. if (anyof((wfs.gsalt != 0)*(wfs.zeropoint == 0)*(wfs.skymag != 0)) && (gs.zeropoint == 0)) {
  646. exit,"You have some LGS with sky background and gs.zeropoint has not been set";
  647. }
  648. // LOOP STRUCTURE
  649. if (loop.method == string()) loop.method = "closed-loop";
  650. loop_method_options = ["closed-loop","open-loop","pseudo open-loop","none"];
  651. if (noneof(loop_method_options == loop.method)) exit,"ERROR: loop.method should be set to closed-loop, open-loop or pseudo open-loop";
  652. if ((loop.method == "open-loop") && (loop.gain != 1 || loop.leak != 1)){
  653. write, "Warning: For open-loop simulations the recommended settings are";
  654. write, "loop.gain = 1. and loop.leak = 1.";
  655. }
  656. if (loop.gain == 0) write,format="%s\n","Warning: loop.gain = 0";
  657. if ( (numberof(*loop.gainho)) != (numberof(*loop.leakho)) ) \
  658. exit,"*loop.gainho should have same number of elements as *loop.leakho";
  659. if (loop.niter == 0) exit,"loop.niter has not been set";
  660. if (loop.ittime == 0) exit,"loop.ittime has not been set";
  661. if (loop.startskip == 0) loop.startskip = 10;
  662. if (loop.skipby == 0) loop.skipby = 10000;
  663. if (loop.modalgainfile == string()) loop.modalgainfile = "";
  664. if (loop.stats_every==0) loop.stats_every=4;
  665. //============================================================================
  666. // DONE WITH BASIC EXISTENCE AND CONSISTENCY CHECKINGS AND DEFAULT ASSIGNMENTS
  667. //============================================================================
  668. // NOW GOING INTO MORE ELABORATE CHECKINGS
  669. // WFSs:
  670. for (ns=1;ns<=nwfs;ns++) {
  671. if (wfs(ns).zeropoint == 0){
  672. wfs(ns)._zeropoint = gs.zeropoint;
  673. } else {
  674. wfs(ns)._zeropoint = wfs(ns).zeropoint;
  675. }
  676. if (wfs(ns).framedelay == -1){
  677. wfs(ns)._framedelay = loop.framedelay;
  678. } else {
  679. wfs(ns)._framedelay = wfs(ns).framedelay;
  680. if ((sim.verbose) && (loop.framedelay != wfs(ns).framedelay)){
  681. write, format="Using a loop delay of %d frames for wfs(%d)\n",wfs(ns).framedelay, ns;
  682. }
  683. }
  684. if (wfs(ns).lambda < 0.1) {
  685. write,format="WFS#%d: wfs.lambda < 0.1. That seems weird.\n",ns;
  686. write,"Remember: lambda should be in microns";
  687. exit;
  688. }
  689. if (wfs(ns).shthreshold < 0.) {
  690. write,format="WFS#%d: wfs.shthreshold < 0 does not make sense\n",ns;
  691. exit;
  692. }
  693. if ((wfs(ns).filtertilt == 1) && (wfs(ns).correctUpTT == 0)) {
  694. write,format="WARNING! WFS#%d: wfs.correctUpTT = 0 with wfs.filtertilt = 1\n",ns;
  695. }
  696. if ((wfs(ns).correctUpTT == 1) && (wfs(ns).uplinkgain == 0)) {
  697. write,format="WARNING! WFS#%d: wfs.correctUpTT = 1 but wfs.uplinkgain = 0\n",ns;
  698. }
  699. // for shmethod = 1, we still need to set npixels and pixsize to avoid crashing the
  700. // WFS initialization routine that is still used to set other parameters used with
  701. // method 1. So we put DUMMY VALUES.
  702. if (wfs(ns).shmethod == 1) {
  703. if (wfs(ns).pixsize == 0){wfs(ns).pixsize = 0.1;}
  704. if (wfs(ns).npixels == 0){wfs(ns).npixels = 2;}
  705. }
  706. if ( (wfs(ns).type=="curvature") && (wfs(ns).nintegcycles != 1) ) {
  707. write,"\n I have not implemented yet nintegcycle > 1 for curvature WFS.";
  708. write,"I can see no reason to do it, as curvature sensors are usually";
  709. write,"read-out noise free, therefore reducing the gain should be";
  710. write,"prefered. If you have a compelling reason and want it, drop";
  711. write,"me an email.";
  712. exit;
  713. }
  714. // Are we using a WFS we know?
  715. wfs_type = strtolower(wfs(ns).type);
  716. if ( (wfs_type != "curvature") && (wfs_type != "hartmann") &&
  717. (wfs_type != "zernike") && (wfs_type !="kl") &&
  718. (wfs_type != "pyramid") && (wfs_type !="dh")) {
  719. // check if this is a user supplied function
  720. cmd = swrite(format="totype = typeof(%s)",wfs(ns).type);
  721. include,[cmd],1;
  722. if ( totype != "function") {
  723. error,swrite(format="wfs(%d).type : Unknown value or non-existing function \"%s\"",
  724. ns,wfs(ns).type)
  725. }
  726. } else wfs(ns).type = wfs_type;
  727. }
  728. // Sets the Influence function file name if not set:
  729. for (nm=1;nm<=ndm;nm++) {
  730. if (dm(nm).iffile == "") {
  731. dm(nm).iffile = parprefix+swrite(format="-if%d",nm)+".fits";
  732. write,format="dm(%d).iffile set to %s\n",nm,dm(nm).iffile;
  733. }
  734. if (dm(nm).ecmatfile == "") {
  735. dm(nm).ecmatfile = parprefix+swrite(format="-ecmat%d",nm)+".fits";
  736. }
  737. if ((dm(nm).type != "stackarray") && (dm(nm).elt == 1)) {
  738. exit,swrite(format="DM %d: parameter dm.elt only used with stackarray mirrors\n",nm);
  739. }
  740. // Are we using a DM we know?
  741. dm_type = strtolower(dm(nm).type);
  742. if ( (dm_type != "bimorph") && (dm_type != "stackarray") &&
  743. (dm_type != "zernike") && (dm_type != "kl") &&
  744. (dm_type != "segmented") && (dm_type != "dh") &&
  745. (dm_type != "tiptilt") && (dm_type != "aniso") ) {
  746. // check if this is a user supplied function
  747. cmd = swrite(format="totype = typeof(%s)",dm(nm).type);
  748. include,[cmd],1;
  749. if ( totype != "function") {
  750. error,swrite(format="dm(%d).type : Unknown value or non-existing function \"%s\"",
  751. nm,dm(nm).type)
  752. }
  753. } else dm(nm).type = dm_type;
  754. dm(nm)._eiffile = parprefix+swrite(format="-if%d",nm)+"-ext.fits";
  755. }
  756. for (i=1;i<=max(_(wfs.subsystem,dm.subsystem));i++) {
  757. if (noneof(wfs.subsystem == i)) {
  758. exit,swrite(format="There is no WFS in subsystem %d\n",i);
  759. }
  760. if (noneof(dm.subsystem == i)) {
  761. exit,swrite(format="There is no DM in subsystem %d\n",i);
  762. }
  763. }
  764. // Sets the interaction/command matrix file name if not set:
  765. if (mat.file == "") {mat.file = parprefix+"-mat.fits";}
  766. // # of targets (stars at which the performance are evaluated):
  767. target.xposition = &(float(*target.xposition));
  768. target.yposition = &(float(*target.yposition));
  769. target._ntarget = numberof(*target.xposition);
  770. target._nlambda = numberof(*target.lambda);
  771. if (anyof((*target.lambda) < 0.1)) {
  772. write,"Some or all target.lambda < 0.1. That seems weird.";
  773. write,"Remember: lambda should be in microns";
  774. exit;
  775. }
  776. if (opt!=[]) {
  777. for (no=1;no<=noptics;no++) {
  778. if (opt(no).misreg==[]) {
  779. noptics = numberof(opt(no).phasemaps);
  780. for (i=1;i<=noptics;i++) opt(no).misreg = [0.,0.];
  781. }
  782. opt(no).misreg= float(opt(no).misreg);
  783. if ((opt(no).path_type=="wfs")&&(opt.path_which==[])) opt.path_which = &indgen(nwfs);
  784. if ((opt(no).path_type=="target")&&(opt.path_which==[])) opt.path_which = &indgen(ntarget);
  785. if ((opt(no).path_type)&&(opt(no).path_type=="")) opt(no).path_type="common";
  786. if ((opt(no).path_type)&&(opt(no).path_type!="wfs")&&\
  787. (opt(no).path_type!="target")&&(opt(no).path_type!="common")) \
  788. error,swrite(format="Unknown optics path \"%s\".\n It should be \"common\", \"wfs\" or \"target\" (default=\"common\")",opt(no).path_type);
  789. }
  790. }
  791. if (sim.verbose) write,format="%s\n","Check parameters: OK";
  792. }
  793. //----------------------------------------------------
  794. func disp2d(ar,xpos,ypos,area,zoom=,power=,init=,nolimits=)
  795. /* DOCUMENT func disp2d(arrayptr,xpos,ypos,area,zoom=,power=,init=)
  796. display several images in the same plsys, at position given
  797. by xpos and ypos.
  798. ar: ar can be either an array of pointers or an image cube
  799. xpos,ypos: the (X,Y) positions, in arbitrary coordinates
  800. area: plsys number
  801. zoom: keyword (vector, dim nim), additional zooming factor (on top of default)
  802. power: *arrayptr^power is displayed
  803. init: initialize, precompute stuff
  804. SEE ALSO:
  805. */
  806. {
  807. extern basezoomptr;
  808. plsys,area;
  809. if (typeof(ar) == "pointer") {
  810. cas = "ptr";
  811. nim = numberof(ar);
  812. earlyExit = (*ar(1) == []); // in case we call init with ar undefined (legitimate)
  813. } else {
  814. cas = "cube";
  815. tmp = dimsof(ar);
  816. if (tmp(1) == 2) {nim=1;} else {nim=tmp(4);}
  817. }
  818. if (is_set(init)) {
  819. if (basezoomptr == []) {
  820. basezoomptr=array(pointer,10);
  821. }
  822. if ((zoom != []) && (numberof(zoom) != nim)) {zoom = array(zoom,nim);}
  823. if (zoom == []) {zoom = array(1.,nim);}
  824. xd = abs(xpos-xpos(-,));
  825. yd = abs(ypos-ypos(-,));
  826. di = sqrt(xd^2.+yd^2.);
  827. di = di+unit(nim)*max(di);
  828. w = where(zoom!=0);
  829. basezoom = (1.+0.9*min(di(w))/2.)*zoom;
  830. basezoomptr(area) = &basezoom;
  831. if (!is_set(nolimits)) {
  832. limits,min(xpos(w)-basezoom(w)),max(xpos(w)+basezoom(w)),
  833. min(ypos(w)-basezoom(w)),max(ypos(w)+basezoom(w)),square=1;
  834. }
  835. if (earlyExit) return;
  836. }
  837. basezoom = *basezoomptr(area);
  838. if ( cas=="ptr") {
  839. if (!is_set(power)) {
  840. for (i=1;i<=nim;i++) {
  841. if (basezoom(i)==0) continue;
  842. off = basezoom(i)/(dimsof(*ar(1))(2));
  843. pli,bytscl(*ar(i),cmin=0),xpos(i)-basezoom(i)-off,ypos(i)-basezoom(i)-off,
  844. xpos(i)+basezoom(i)-off,ypos(i)+basezoom(i)-off;
  845. }
  846. } else {
  847. for (i=1;i<=nim;i++) {
  848. if (basezoom(i)==0) continue;
  849. off = basezoom(i)/(dimsof(*ar(1))(2));
  850. pli,bytscl(*ar(i)^power,cmin=0),xpos(i)-basezoom(i)-off,ypos(i)-basezoom(i)-off,
  851. xpos(i)+basezoom(i)-off,ypos(i)+basezoom(i)-off;
  852. }
  853. }
  854. } else {
  855. if (!is_set(power)) {
  856. for (i=1;i<=nim;i++) {
  857. if (basezoom(i)==0) continue;
  858. off = basezoom(i)/(dimsof(ar(,,1))(2));
  859. pli,bytscl(ar(,,i),cmin=0),xpos(i)-basezoom(i)-off,ypos(i)-basezoom(i)-off,
  860. xpos(i)+basezoom(i)-off,ypos(i)+basezoom(i)-off;
  861. }
  862. } else {
  863. for (i=1;i<=nim;i++) {
  864. if (basezoom(i)==0) continue;
  865. off = basezoom(i)/(dimsof(ar(,,1))(2));
  866. pli,bytscl(ar(,,i)^power,cmin=0),xpos(i)-basezoom(i)-off,ypos(i)-basezoom(i)-off,
  867. xpos(i)+basezoom(i)-off,ypos(i)+basezoom(i)-off;
  868. }
  869. }
  870. }
  871. if ((is_set(init)) && (!is_set(nolimits))) {
  872. limits,square=1;
  873. limits;
  874. }
  875. }
  876. //--------------------------------------------------------------------------
  877. func modal_gain_optimization(disp=,update=)
  878. /* DOCUMENT func modal_gain_optimization(disp=,update=)
  879. NOT UPGRADED TO VERSION 2.
  880. DO NOT USE.
  881. This routine optimizes the modal gains.
  882. Keywords:
  883. disp: set to display stuff
  884. update: set if this is a gain update (opposite to the first time run)
  885. This routine uses:
  886. - the saved error circular buffer (cberr.fits)
  887. This routine calls:
  888. ...
  889. This routine sets:
  890. ...
  891. SEE ALSO:
  892. */
  893. {
  894. cberr = yao_fitsread("cberr.fits"); // CB of actuator error
  895. nGoodSamples = long(2^floor(log(ao.LoopNIter-ao.LoopStartSkip)/log(2)));
  896. cbmoderr = atm(,+)*cberr(+,1-nGoodSamples:);// CB of mode coef errors
  897. modgains = modalgain*ao.LoopGain; // overall mode gains
  898. bestGains = modgains*0.;
  899. length = 2048;
  900. gainNpt = 30;
  901. toGains = spanl(1e-2,0.9,gainNpt);
  902. errvect = array(float,gainNpt);
  903. errTF = array(float,length/2-1,gainNpt);
  904. fudgeFactor = 0.85;
  905. // initialize Error Transfer functions:
  906. for (n=1;n<=gainNpt;n++)
  907. {errTF(,n) = ft_cb_ao_simul(ao.LoopFrameDelay,toGains(n),length/2)(,1);}
  908. // Loop over controlled modes:
  909. for (i=1;i<=NModesControlled;i++)
  910. {
  911. // Build PSD of the error
  912. psderr = psd(cbmoderr(i,),length,samp=ao.LoopItTime,filter=6,silent=1)(2:,2);
  913. pause,10;
  914. // ^^ psderr is in fact length/2 long
  915. // Build current gain transfer function
  916. curErrTF = ft_cb_ao_simul(ao.LoopFrameDelay,modgains(i),length/2)(,1);
  917. // Reconstruct open loop PSD
  918. OLmodPsd = psderr/curErrTF;
  919. // Loop over possible gains
  920. for (n=1;n<=gainNpt;n++) {errvect(n) = sum(OLmodPsd*errTF(,n));}
  921. if (is_set(disp)) {logxy,0,0; plot,errvect,toGains; pause,500;}
  922. // Determine best gain
  923. bestGains(i) = fudgeFactor*toGains(errvect(mnx));
  924. if (ao.verbose>=1) {write,format="Mode %i, gain to be updated from %f to %f\n",
  925. i,modgains(i),bestGains(i);}
  926. }
  927. // Update modalgain
  928. if (is_set(update))
  929. {
  930. modalgain = bestGains/ao.LoopGain;
  931. yao_fitswrite,YAO_SAVEPATH+ao.LoopModalGainFile,modalgain;
  932. if (ao.verbose>=1) {write,format="Gains updated and saved in !",ao.LoopModalGainFile;}
  933. }
  934. }
  935. //----------------------------------------------------
  936. func ft_cb_ao_simul(FrameDelay,gain,dim)
  937. /* DOCUMENT func ft_cb_ao_simul(FrameDelay,gain,dim)
  938. NOT UPGRADED TO VERSION 2.
  939. DO NOT USE UNTIL UPGRADED.
  940. This routine simulates the time aspect of the numerical loop
  941. and compute the associated transfer functions (error, closeloop)
  942. for further use in modal_gain_optimization().
  943. Inputs:
  944. FrameDelay: frame delay in close loop.
  945. gain: AO loop gain
  946. dim: desired linear size of the output transfer functions
  947. SEE ALSO: modal_gain_optimization
  948. */
  949. {
  950. input = array(float,2*dim);
  951. mirvect = array(float,2*dim);
  952. output = array(float,2*dim);
  953. input(2) = 1.;
  954. mir = 0.;
  955. command = 0.;
  956. wfsMesHistory = array(float,FrameDelay+1);
  957. for (i=1;i<=2*dim;i++)
  958. {
  959. in = input(i);
  960. // Subtract the mirror shape:
  961. out = in - mir;
  962. // Do the WFS:
  963. mes = out;
  964. // Handling frame delay (0 -> no frame delay at all, 1 -> regular
  965. // one frame delay of integrator, 2 -> integrator + one frame
  966. // computation, etc...)
  967. wfsMesHistory = roll(wfsMesHistory,-1);
  968. wfsMesHistory(FrameDelay+1) = mes;
  969. usedMes = wfsMesHistory(1);
  970. // computes the command vector (integrator with gain):
  971. err = usedMes;
  972. command += gain*err;
  973. // Computes the mirror shape using influence functions:
  974. mir = command;
  975. // Subtract the mirror shape:
  976. out = in - mir;
  977. mirvect(i) = command;
  978. output(i) = err;
  979. // write,input(i),command,err,out; pause,500;
  980. }
  981. errTF = abs((fft(output+1e-12,1))^2.)/abs((fft(input,1))^2.);
  982. errTF = errTF(2:dim);
  983. clTF = abs((fft(mirvect,1))^2.)/abs((fft(input,1))^2.);
  984. clTF = clTF(2:dim);
  985. return [errTF,clTF];
  986. }
  987. //----------------------------------------------------
  988. func ss_noise(nss)
  989. {
  990. write,format="Computing propagated noise for subsystem %d\n",nss;
  991. write,"Extracting cmat";
  992. sswfs = ssdm = [];
  993. for (ns=1;ns<=nwfs;ns++) {grow,sswfs,array(wfs(ns).subsystem,wfs(ns)._nmes);}
  994. for (nm=1;nm<=ndm;nm++) {grow,ssdm,array(dm(nm).subsystem,dm(nm)._nact);}
  995. wsswfs = where(sswfs == nss);
  996. wssdm = where(ssdm == nss);
  997. cmat = cMat(wssdm,wsswfs);
  998. write,"Computing subsystem modes";
  999. nmv = where(dm.subsystem == nss);
  1000. if (numberof(nmv)==0) error,"No DM found in subsystems";
  1001. modes = build_dm_modes(nmv,actmodes,modvar);
  1002. tmp = actmodes(+,)*cmat(+,);
  1003. modcov = trace(tmp(,+)*tmp(,+));
  1004. // now the measurements are in arcsec, so we got to convert to rd of
  1005. // phase difference at the edge of the subaperture, which is what
  1006. // the SH measure:
  1007. nsv = where(wfs.subsystem == nss);
  1008. if (numberof(nsv) != 1) error,"zero or more than one wfs in subsystem";
  1009. ns = nsv(1);
  1010. // subsize in pixel:
  1011. subsize = sim.pupildiam/wfs(ns).shnxsub(0);
  1012. if (wfs(ns).npixpersub) subsize = wfs(ns).npixpersub;
  1013. // subsize in meters:
  1014. subsize_m = subsize * tel.diam/sim.pupildiam;
  1015. //subsize = tel.diam/wfs(ns).shnxsub;
  1016. arcsectord = 4.848e-6*subsize_m*2*pi/wfs(ns).lambda/1e-6;
  1017. noise = modcov*modvar/arcsectord^2.;
  1018. write,format="Total noise on phase for 1rd2 per subaperture = %f rd2\n",sum(noise);
  1019. fma;plh,noise; limits,square=0; limits;
  1020. return noise;
  1021. }
  1022. //----------------------------------------------------
  1023. func build_dm_modes(nm,&u,&modvar,&eigenvalues,disp=)
  1024. /* DOCUMENT unfinished, I think.
  1025. NOT FINISHED, NOT UPGRADED TO VERSION 2.
  1026. DO NOT USE.
  1027. */
  1028. {
  1029. if (anyof(dm(nm).elt)) exit,"Not implemented for dm.elt=1";
  1030. def = [];
  1031. for (i=1;i<=numberof(nm);i++) {
  1032. grow,def,*dm(nm(i))._def;
  1033. }
  1034. defpup = ipupil(dm(nm(1))._n1:dm(nm(1))._n2,dm(nm(1))._n1:dm(nm(1))._n2);
  1035. wpup = where(defpup);
  1036. tmp = def(*,)(wpup,); // not really saving RAM, but...
  1037. tpup = sum(defpup);
  1038. /*
  1039. p = (def*ipupil)(sum,sum,)/tpup;
  1040. def = def-p(-,-,);
  1041. def = reform(def,long(ao._size)*ao._size,ao._DmNAct);
  1042. def = def(where(ipupil),);
  1043. */
  1044. dd = tmp(+,)*tmp(+,);
  1045. eigenvalues = SVdec(dd,u,vt);
  1046. modes = def(,,+)*u(+,);
  1047. // mask with ipupil
  1048. modes = modes*defpup(,,-);
  1049. // compute mode variance:
  1050. modvar = (modes(*,)(wpup,)(rms,))^2.;
  1051. return modes;
  1052. if (disp == 1)
  1053. {for (i=1;i<=ao._DmNAct;i++) {fma; pli,modes(,,i)*ipupil;pause,100;}}
  1054. //normalize the modes:
  1055. norm = sqrt((modes^2.*ipupil)(avg,avg,));
  1056. modes = modes/norm(-,-,);
  1057. ActIF = modes(,,1:-1) //*(1./indgen(ao._DmNAct-1))(-,-,);
  1058. ao._DmNAct = ao._DmNAct-1;
  1059. do_imat,disp=1;
  1060. build_cmat,all=1,nomodalgain=1;
  1061. mn = cMat(,+)*cMat(,+);
  1062. mn = diag(mn)*indgen(ao._DmNAct-1)^2.
  1063. if (disp == 1) {fma;plg,mn;}
  1064. return modes;
  1065. }
  1066. //----------------------------------------------------
  1067. func make_curv_wfs_subs(ns,dim,pupd,disp=,cobs=)
  1068. /* DOCUMENT func make_curv_wfs_subs(ns,dim,pupd,disp=)
  1069. */
  1070. {
  1071. local WhichRing,SubThetaIn,SubThetaOut,SubRadiusIn,SubRadiusOut;
  1072. NRing = sum(*(wfs(ns).nsubperring) != 0); // Number of Rings
  1073. NSubPerRing = (*(wfs(ns).nsubperring))(1:NRing);
  1074. NSub = sum(NSubPerRing);
  1075. // Compute the internal and external radius of each rings
  1076. // given the number of subapertures per rings:
  1077. SurfOneSub = pi/sum(NSubPerRing); // Surface of one actuator
  1078. RInRing = array(float,NRing); // Internal Radius
  1079. ROutRing = array(float,NRing); // External Radius
  1080. if (is_set(cobs)) {RInRing(1) = cobs;}
  1081. // loop on ring number
  1082. for (i=1;i<=NRing;i++) {
  1083. ROutRing(i) = sqrt(NSubPerRing(i)*SurfOneSub/pi+RInRing(i)^2.);
  1084. if (i != NRing) RInRing(i+1) = ROutRing(i);
  1085. }
  1086. if (is_set(cobs)) {RInRing(1) = 0.;}
  1087. ROutRing(NRing) = 1.6;
  1088. // now we got to determine the inner and outer radius and angle for
  1089. // each actuators:
  1090. WhichRing = array(1,NSubPerRing(1)); // Ring index per actuator
  1091. for (i=2;i<=NRing;i++) {grow,WhichRing,array(i,NSubPerRing(i));}
  1092. // offset angle of first subaperture in rings:
  1093. if (*wfs(ns).angleoffset==[]) angleoffset=array(0.,NRing);
  1094. else angleoffset=(*wfs(ns).angleoffset)*pi/180.;
  1095. // if rint and rout are specified, use it instead:
  1096. if ((*wfs(ns).rint)!=[]) RInRing=*wfs(ns).rint;
  1097. if ((*wfs(ns).rout)!=[]) ROutRing=*wfs(ns).rout;
  1098. // loop to determine radiuses and angle:
  1099. for (i=1;i<=NRing;i++) {
  1100. dtheta = 2*pi/NSubPerRing(i);
  1101. for (j=1;j<=NSubPerRing(i);j++) {
  1102. t1 = (j-1.)*dtheta + angleoffset(i);
  1103. t2 = t1+dtheta;
  1104. grow,SubThetaIn,t1 ;
  1105. grow,SubThetaOut,t2 ;
  1106. grow,SubRadiusIn,RInRing(i) ;
  1107. grow,SubRadiusOut,ROutRing(i) ;
  1108. }
  1109. }
  1110. // Now build the subapertures images:
  1111. x = span(1,dim,dim)(,-:1:dim)-dim/2.-1;
  1112. y = transpose(x);
  1113. ang = atan(y,x);
  1114. ang1 = ang + (ang < 0)*2*pi;
  1115. ang2 = (ang1+pi)%(2*pi)+pi;
  1116. rad = dist(dim)/(pupd/2.);
  1117. d2 = eclat(dist(dim)^2.);
  1118. Subs = array(short,dim,dim,NSub);
  1119. for (i=1;i<=NSub;i++) {
  1120. // the following to avoid issues due to discontinuity of ang array at 0=2pi
  1121. if (SubThetaOut(i)>(2*pi)) ang=ang2; else ang=ang1;
  1122. tmp = (rad >= SubRadiusIn(i)) * (rad < SubRadiusOut(i)) * \
  1123. (ang >= SubThetaIn(i)) * (ang < SubThetaOut(i));
  1124. if (i>1) tmp = tmp-Subs(,,i-1); // avoid double points
  1125. Subs(,,i) = (tmp==1);
  1126. if (disp == 1) {fma; pli,tmp;}
  1127. }
  1128. // ok, in the following, I have changed the way one sums
  1129. // the signal over the subaperture area in CurvWFS:
  1130. // now I am passing a vector of indices for each subaperture
  1131. // and using them for the sum.
  1132. // sind is the ensemble of index vectors
  1133. // nsind is the number of actual indices for a given subaperture
  1134. tmp = Subs(sum,sum,);
  1135. sind = array(long,max(tmp),NSub)*0+1;
  1136. nsind = array(long,NSub);
  1137. for (i=1;i<=NSub;i++)
  1138. {
  1139. sind(1:tmp(i),i) = where(Subs(,,i) == 1);
  1140. nsind(i) = sum(Subs(,,i));
  1141. }
  1142. wfs(ns)._sind = &(int(sind));
  1143. wfs(ns)._nsind = &(int(nsind));
  1144. return Subs;
  1145. }
  1146. //----------------------------------------------------
  1147. func _map2d(t,dim,cent)
  1148. {
  1149. // assumes XY square array as input
  1150. curdim = dimsof(t);
  1151. if (curdim(2) != curdim(3)) {exit,"_map2d only takes square arrays";}
  1152. cdim = curdim(2);
  1153. if (cdim == dim) {return t;}
  1154. if (cdim > dim) {
  1155. if (is_void(cent)) {cent = cdim/2+1.;}
  1156. _p1 = long(ceil(cent-dim/2.));
  1157. _p2 = _p1+dim-1;
  1158. return t(_p1:_p2,_p1:_p2,..);
  1159. }
  1160. if (cdim < dim) {
  1161. if (is_void(cent)) {cent = dim/2+1.;}
  1162. curdim(2:3) = [dim,dim];
  1163. tmp = array(structof(t),curdim);
  1164. _p1 = long(ceil(cent-cdim/2.));
  1165. _p2 = _p1+cdim-1;
  1166. if (_p2 != long(floor(cent+cdim/2.-1))) {exit,"Problem in _map2d";}
  1167. tmp(_p1:_p2,_p1:_p2,..) = t;
  1168. return tmp;
  1169. }
  1170. }
  1171. //----------------------------------------------------
  1172. func noll(ord)
  1173. {
  1174. /* DOCUMENT func noll(ord)
  1175. Noll variance of zernike for D/r0 = 1
  1176. SEE ALSO:
  1177. */
  1178. innp = 0.;
  1179. cov = array(double,ord);
  1180. for (j=2;j<=ord+1;j++) {
  1181. tmp = zernumero(j); n = tmp(1); m = tmp(2);
  1182. innp = gamma(14./3.)*gamma((2*n-14./3.+3.)/2.)/
  1183. (2.^(14./3.)*gamma((14./3.+1.)/2.)*gamma((14./3.+1.)/2.)*
  1184. gamma((2*n+14./3.+3.)/2.));
  1185. cov(j-1) = (0.046/pi*(n+1.)*innp*(-1.)^((2*n-2*m)/2.));
  1186. }
  1187. return cov*(2.*pi)^(11./3.)/pi;
  1188. }
  1189. //----------------------------------------------------
  1190. func nollmat(ord)
  1191. {
  1192. /* DOCUMENT func nollmat(ord)
  1193. Noll covariance matrix for D/r0 = 1
  1194. SEE ALSO:
  1195. */
  1196. innp = 0.;
  1197. cov = array(double,[2,ord,ord]);
  1198. for (j=2;j<=ord+1;j++) {
  1199. for (jp=2;jp<=ord+1;jp++) {
  1200. tmp = zernumero(j);
  1201. tmpp = zernumero(jp);
  1202. n = tmp(1); m = tmp(2);
  1203. np = tmpp(1); mp = tmpp(2);
  1204. //print,(n+np-14./3.+3.)/2.,(np-n+14./3.+1.)/2.,
  1205. // (n-np+14./3.+1.)/2.,(n+np+14./3.+3.)/2.;
  1206. innp = gamma(14./3.)*gamma((n+np-14./3.+3.)/2.)/
  1207. (2.^(14./3.)*gamma((np-n+14./3.+1.)/2.)*gamma((n-np+14./3.+1.)/2.)*
  1208. gamma((n+np+14./3.+3.)/2.));
  1209. cov(j-1,jp-1) = (0.046/pi*sqrt((n+1.)*(np+1.))*innp*float(m == mp)*
  1210. (-1.)^(float

Large files files are truncated, but you can click here to view the full file